@@ -21,16 +21,19 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"os"
24
+ "sort"
24
25
25
26
appsv1 "k8s.io/api/apps/v1"
26
27
corev1 "k8s.io/api/core/v1"
27
28
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
30
"k8s.io/apimachinery/pkg/runtime"
30
31
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
32
+ "k8s.io/apimachinery/pkg/util/version"
31
33
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
32
34
"k8s.io/client-go/tools/clientcmd"
33
35
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
36
+ "sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
34
37
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
35
38
36
39
admissionv1 "k8s.io/api/admissionregistration/v1"
@@ -67,6 +70,8 @@ type genericProviderList interface {
67
70
operatorv1.GenericProviderList
68
71
}
69
72
73
+ var errNotFound = errors .New ("404 Not Found" )
74
+
70
75
// CreateKubeClient creates a kubernetes client from provided kubeconfig and kubecontext.
71
76
func CreateKubeClient (kubeconfigPath , kubeconfigContext string ) (ctrlclient.Client , error ) {
72
77
// Use specified kubeconfig path and context
@@ -181,3 +186,75 @@ func NewGenericProvider(providerType clusterctlv1.ProviderType) operatorv1.Gener
181
186
panic (fmt .Sprintf ("unknown provider type %s" , providerType ))
182
187
}
183
188
}
189
+
190
+ // GetLatestRelease returns the latest patch release.
191
+ func GetLatestRelease (ctx context.Context , repo repository.Repository ) (string , error ) {
192
+ versions , err := repo .GetVersions (ctx )
193
+ if err != nil {
194
+ return "" , fmt .Errorf ("failed to get repository versions: %w" , err )
195
+ }
196
+
197
+ // Search for the latest release according to semantic version ordering.
198
+ // Releases with tag name that are not in semver format are ignored.
199
+ parsedReleaseVersions := []* version.Version {}
200
+
201
+ for _ , v := range versions {
202
+ sv , err := version .ParseSemantic (v )
203
+ if err != nil {
204
+ // discard releases with tags that are not a valid semantic versions (the user can point explicitly to such releases)
205
+ continue
206
+ }
207
+
208
+ parsedReleaseVersions = append (parsedReleaseVersions , sv )
209
+ }
210
+
211
+ versionCandidates := parsedReleaseVersions
212
+
213
+ if len (parsedReleaseVersions ) == 0 {
214
+ return "" , errors .New ("failed to find releases tagged with a valid semantic version number" )
215
+ }
216
+
217
+ // Sort parsed versions by semantic version order.
218
+ sort .SliceStable (versionCandidates , func (i , j int ) bool {
219
+ // Prioritize release versions over pre-releases. For example v1.0.0 > v2.0.0-alpha
220
+ // If both are pre-releases, sort by semantic version order as usual.
221
+ if versionCandidates [j ].PreRelease () == "" && versionCandidates [i ].PreRelease () != "" {
222
+ return false
223
+ }
224
+ if versionCandidates [i ].PreRelease () == "" && versionCandidates [j ].PreRelease () != "" {
225
+ return true
226
+ }
227
+
228
+ return versionCandidates [j ].LessThan (versionCandidates [i ])
229
+ })
230
+
231
+ // Limit the number of searchable versions by 3.
232
+ size := 3
233
+ if size > len (versionCandidates ) {
234
+ size = len (versionCandidates )
235
+ }
236
+
237
+ versionCandidates = versionCandidates [:size ]
238
+
239
+ for _ , v := range versionCandidates {
240
+ // Iterate through sorted versions and try to fetch a file from that release.
241
+ // If it's completed successfully, we get the latest release.
242
+ // Note: the fetched file will be cached and next time we will get it from the cache.
243
+ versionString := "v" + v .String ()
244
+
245
+ _ , err := repo .GetFile (ctx , versionString , repo .ComponentsPath ())
246
+ if err != nil {
247
+ if errors .Is (err , errNotFound ) {
248
+ // Ignore this version
249
+ continue
250
+ }
251
+
252
+ return "" , err
253
+ }
254
+
255
+ return versionString , nil
256
+ }
257
+
258
+ // If we reached this point, it means we didn't find any release.
259
+ return "" , errors .New ("failed to find releases tagged with a valid semantic version number" )
260
+ }
0 commit comments