Skip to content

Commit 846c0e4

Browse files
committed
feat: add check metadata command to clusterctl
This adds a new command to clusterctl which given a version number will check that there is a release series defined in a metadata file for that version. The main use case for this is a CI check. When building a release of a provider that is for a new major or minor version its required that a new release series is added to the providers metadata file. This ensures that the correct version of the provider is installed when initializing a management cluster. A command failure for release pipelines is that the metadata file hasn't been updated. Signed-off-by: Richard Case <[email protected]>
1 parent d43e0ac commit 846c0e4

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"strings"
23+
24+
"github.com/blang/semver/v4"
25+
"github.com/spf13/cobra"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/runtime/serializer"
28+
"sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/internal/templates"
29+
30+
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
31+
)
32+
33+
type checkMetadataOptions struct {
34+
file string
35+
version string
36+
}
37+
38+
var (
39+
checkMetadataOpts = &checkMetadataOptions{}
40+
41+
checkMetadataLong = templates.LongDesc(`
42+
Command that will check that the given metadata file contains an entry
43+
for the supplied version number.
44+
45+
This is intended to be used in CI to fail a release build early if
46+
there is no metadata entry for the release version series.
47+
`)
48+
49+
checkMetadataExample = templates.Examples(`
50+
clusterctl check-metadata -f metadata.yaml --version v1.0.17
51+
`)
52+
53+
checkMetadataCmd = &cobra.Command{
54+
Use: "check-metadata",
55+
GroupID: groupOther,
56+
Short: "Validate the metadata file given a version number",
57+
Long: templates.LongDesc(checkMetadataLong),
58+
Example: checkMetadataExample,
59+
Args: cobra.NoArgs,
60+
RunE: func(cmd *cobra.Command, args []string) error {
61+
return runCheckMetadata()
62+
},
63+
}
64+
)
65+
66+
func init() {
67+
checkMetadataCmd.Flags().StringVarP(&checkMetadataOpts.file, "file", "f", "", "Path to metadata.yaml file to check")
68+
checkMetadataCmd.Flags().StringVar(&checkMetadataOpts.version, "version", "", "The version number to check against the metadata file")
69+
70+
checkMetadataCmd.MarkFlagRequired("file")
71+
checkMetadataCmd.MarkFlagRequired("version")
72+
73+
RootCmd.AddCommand(checkMetadataCmd)
74+
}
75+
76+
func runCheckMetadata() error {
77+
versionToCheck := strings.TrimPrefix(checkMetadataOpts.version, "v")
78+
79+
ver, err := semver.Parse(versionToCheck)
80+
if err != nil {
81+
return fmt.Errorf("failed parsing %s as semver version: %w", checkMetadataOpts.version, err)
82+
}
83+
84+
fileData, err := os.ReadFile(checkMetadataOpts.file)
85+
if err != nil {
86+
return fmt.Errorf("failed reading file %s: %w", checkMetadataOpts.file, err)
87+
}
88+
89+
scheme := runtime.NewScheme()
90+
if err := clusterctlv1.AddToScheme(scheme); err != nil {
91+
return fmt.Errorf("failed to add metadata type to scheme: %w", err)
92+
}
93+
metadata := &clusterctlv1.Metadata{}
94+
codecFactory := serializer.NewCodecFactory(scheme)
95+
96+
if err := runtime.DecodeInto(codecFactory.UniversalDecoder(), fileData, metadata); err != nil {
97+
return fmt.Errorf("failed decoding metadata from file: %w", err)
98+
}
99+
100+
for _, series := range metadata.ReleaseSeries {
101+
if series.Major == int32(ver.Major) && series.Minor == int32(ver.Minor) {
102+
fmt.Printf("Success, there is a release series defined in the metadata file\n")
103+
return nil
104+
}
105+
}
106+
107+
return fmt.Errorf("failed to find release series %s in metadata files %s", "", checkMetadataOpts.file)
108+
}

0 commit comments

Comments
 (0)