diff --git a/pkg/clusterstack/metadata.go b/pkg/clusterstack/metadata.go index 0141f480..3a6814a0 100644 --- a/pkg/clusterstack/metadata.go +++ b/pkg/clusterstack/metadata.go @@ -28,6 +28,7 @@ import ( type Component struct { ClusterAddon string `yaml:"clusterAddon"` NodeImage string `yaml:"nodeImage,omitempty"` + ClusterClass string `yaml:"clusterClass"` } // Versions contains version information. diff --git a/pkg/clusterstack/mode.go b/pkg/clusterstack/mode.go index 9db57dd8..0fff3a67 100644 --- a/pkg/clusterstack/mode.go +++ b/pkg/clusterstack/mode.go @@ -35,7 +35,9 @@ func HandleStableMode(gitHubReleasePath string, currentReleaseHash, latestReleas return nil, fmt.Errorf("failed to bump cluster stack: %w", err) } - if currentReleaseHash.ClusterAddonDir != latestReleaseHash.ClusterAddonDir || currentReleaseHash.ClusterAddonValues != latestReleaseHash.ClusterAddonValues { + if currentReleaseHash.ClusterAddonDir != latestReleaseHash.ClusterAddonDir || + currentReleaseHash.ClusterAddonValues != latestReleaseHash.ClusterAddonValues || + currentReleaseHash.ClusterClassDir != latestReleaseHash.ClusterClassDir { metadata.Versions.Components.ClusterAddon, err = BumpVersion(metadata.Versions.Components.ClusterAddon) if err != nil { return nil, fmt.Errorf("failed to bump cluster addon: %w", err) @@ -58,6 +60,22 @@ func HandleStableMode(gitHubReleasePath string, currentReleaseHash, latestReleas fmt.Printf("NodeImage Version unchanged: %s\n", metadata.Versions.Components.NodeImage) } } + if currentReleaseHash.ClusterClassDir != latestReleaseHash.ClusterClassDir { + metadata.Versions.Components.ClusterClass, err = BumpVersion(metadata.Versions.Components.ClusterClass) + if err != nil { + metadata.Versions.Components.ClusterClass = "v1" + fmt.Printf("Initial ClusterClass Version: %s\n", metadata.Versions.Components.ClusterClass) + //return nil, fmt.Errorf("failed to bump cluster class: %w", err) + } else { + fmt.Printf("Bumped ClusterClass Version: %s\n", metadata.Versions.Components.ClusterClass) + } + } else { + if metadata.Versions.Components.ClusterClass == "" { + fmt.Println("No ClusterClass Version.") + } else { + fmt.Printf("ClusterClass Version unchanged: %s\n", metadata.Versions.Components.ClusterClass) + } + } return metadata, nil } @@ -75,19 +93,24 @@ func HandleHashMode(currentRelease cshash.ReleaseHash, kubernetesVersion string) Components: Component{ ClusterAddon: clusterStackHash, NodeImage: clusterStackHash, + ClusterClass: clusterStackHash, }, }, } } // HandleCustomMode handles custom mode with version for all components. -func HandleCustomMode(kubernetesVersion, clusterStackVersion, clusterAddonVersion, nodeImageVersion string) (*MetaData, error) { +func HandleCustomMode(kubernetesVersion, clusterStackVersion, clusterAddonVersion, + clusterClassVersion, nodeImageVersion string) (*MetaData, error) { if _, err := version.New(clusterStackVersion); err != nil { return nil, fmt.Errorf("failed to verify custom version for cluster stack: %q: %w", clusterStackVersion, err) } if _, err := version.New(clusterAddonVersion); err != nil { return nil, fmt.Errorf("failed to verify custom version for cluster addon: %q: %w", clusterAddonVersion, err) } + if _, err := version.New(clusterClassVersion); err != nil { + return nil, fmt.Errorf("failed to verify custom version for cluster class: %q: %w", clusterClassVersion, err) + } if _, err := version.New(nodeImageVersion); err != nil { return nil, fmt.Errorf("failed to verify custom version for node image: %q: %w", nodeImageVersion, err) } @@ -99,6 +122,7 @@ func HandleCustomMode(kubernetesVersion, clusterStackVersion, clusterAddonVersio ClusterStack: clusterStackVersion, Components: Component{ ClusterAddon: clusterAddonVersion, + ClusterClass: clusterClassVersion, NodeImage: nodeImageVersion, }, }, diff --git a/pkg/cmd/create.go b/pkg/cmd/create.go index 90b0459c..72b7dffa 100644 --- a/pkg/cmd/create.go +++ b/pkg/cmd/create.go @@ -61,6 +61,7 @@ var ( nodeImageRegistry string clusterStackVersion string clusterAddonVersion string + clusterClassVersion string nodeImageVersion string remote string publish bool @@ -95,6 +96,7 @@ func init() { createCmd.Flags().StringVarP(&nodeImageRegistry, "node-image-registry", "r", "", "It defines the node image registry. For example oci://ghcr.io/foo/bar/node-images/staging/") createCmd.Flags().StringVar(&clusterStackVersion, "cluster-stack-version", "", "It is used to specify the semver version for the cluster stack in the custom mode") createCmd.Flags().StringVar(&clusterAddonVersion, "cluster-addon-version", "", "It is used to specify the semver version for the cluster addon in the custom mode") + createCmd.Flags().StringVar(&clusterClassVersion, "cluster-class-version", "", "It is used to specify the semver version for the cluster class in the custom mode") createCmd.Flags().StringVar(&nodeImageVersion, "node-image-version", "", "It is used to specify the semver version for the node images in the custom mode") createCmd.Flags().StringVar(&remote, "remote", "github", "Which remote repository to use and thus which credentials are required. Currently supported are 'github' and 'oci'.") createCmd.Flags().BoolVar(&publish, "publish", false, "Publish release after creation is done. This is only implemented for OCI currently.") @@ -168,6 +170,7 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti createOption.Metadata.Versions.ClusterStack = "v1" createOption.Metadata.Versions.Components.ClusterAddon = "v1" createOption.Metadata.Versions.Components.NodeImage = "v1" + createOption.Metadata.Versions.Components.ClusterClass = "v1" } else { if err := downloadReleaseAssets(ctx, latestRepoRelease, "./.tmp/release/", ac); err != nil { return nil, fmt.Errorf("failed to download release asset: %w", err) @@ -196,8 +199,12 @@ func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOpti if nodeImageVersion == "" { return nil, errors.New("please specify a semver for custom version with --node-image-version flag") } + if clusterClassVersion == "" { + return nil, errors.New("please specify a semver for custom version with --cluster-class-version flag") + } - createOption.Metadata, err = clusterstack.HandleCustomMode(createOption.Config.Config.KubernetesVersion, clusterStackVersion, clusterAddonVersion, nodeImageVersion) + createOption.Metadata, err = clusterstack.HandleCustomMode(createOption.Config.Config.KubernetesVersion, + clusterStackVersion, clusterAddonVersion, clusterClassVersion, nodeImageVersion) if err != nil { return nil, fmt.Errorf("failed to handle custom mode: %w", err) } @@ -257,6 +264,7 @@ func createAction(cmd *cobra.Command, args []string) error { func (c *CreateOptions) validateHash() error { if c.CurrentReleaseHash.ClusterAddonDir == c.LatestReleaseHash.ClusterAddonDir && c.CurrentReleaseHash.ClusterAddonValues == c.LatestReleaseHash.ClusterAddonValues && + c.CurrentReleaseHash.ClusterClassDir == c.LatestReleaseHash.ClusterClassDir && c.CurrentReleaseHash.NodeImageDir == c.LatestReleaseHash.NodeImageDir { return errors.New("no change in the cluster stack") } diff --git a/pkg/cshash/hash.go b/pkg/cshash/hash.go index f8ad01c6..2c659ba2 100644 --- a/pkg/cshash/hash.go +++ b/pkg/cshash/hash.go @@ -35,6 +35,7 @@ const ( clusterAddonDirName = "cluster-addon" nodeImageDirName = "node-image" clusterAddonValuesFileName = "cluster-addon-values.yaml" + clusterClassDirName = "cluster-class" ) // ReleaseHash contains the information of release hash. @@ -42,6 +43,7 @@ type ReleaseHash struct { ClusterStack string `json:"clusterStack"` ClusterAddonDir string `json:"clusterAddonDir"` ClusterAddonValues string `json:"clusterAddonValues"` + ClusterClassDir string `json:"clusterClassDir"` NodeImageDir string `json:"nodeImageDir,omitempty"` } @@ -80,7 +82,9 @@ func GetHash(path string) (ReleaseHash, error) { for _, entry := range entries { entryPath := filepath.Join(path, entry.Name()) - if entry.IsDir() && (entry.Name() == clusterAddonDirName || entry.Name() == nodeImageDirName) { + if entry.IsDir() && (entry.Name() == clusterAddonDirName || + entry.Name() == nodeImageDirName || + entry.Name() == clusterClassDirName) { hash, err := dirhash.HashDir(entryPath, "", dirhash.DefaultHash) if err != nil { return ReleaseHash{}, fmt.Errorf("failed to hash dir: %w", err) @@ -92,6 +96,8 @@ func GetHash(path string) (ReleaseHash, error) { releaseHash.ClusterAddonDir = hash case nodeImageDirName: releaseHash.NodeImageDir = hash + case clusterClassDirName: + releaseHash.ClusterClassDir = hash default: // Should not happen return ReleaseHash{}, fmt.Errorf("unknown name type %s", entryPath) @@ -114,6 +120,7 @@ func GetHash(path string) (ReleaseHash, error) { func (r ReleaseHash) ValidateWithLatestReleaseHash(latestReleaseHash ReleaseHash) error { if r.ClusterAddonDir == latestReleaseHash.ClusterAddonDir && r.ClusterAddonValues == latestReleaseHash.ClusterAddonValues && + r.ClusterClassDir == latestReleaseHash.ClusterClassDir && r.NodeImageDir == latestReleaseHash.NodeImageDir { return errors.New("no change in the cluster stack") } diff --git a/vendor/github.com/SovereignCloudStack/cluster-stack-operator/pkg/clusterstack/clusterstack.go b/vendor/github.com/SovereignCloudStack/cluster-stack-operator/pkg/clusterstack/clusterstack.go index 47f5b99e..2f97afa1 100644 --- a/vendor/github.com/SovereignCloudStack/cluster-stack-operator/pkg/clusterstack/clusterstack.go +++ b/vendor/github.com/SovereignCloudStack/cluster-stack-operator/pkg/clusterstack/clusterstack.go @@ -59,13 +59,24 @@ var ( // e.g. - "docker-ferrol-1-27-v1", "docker-ferrol-1-27-v1-alpha.1", etc. func NewFromClusterClassProperties(str string) (ClusterStack, error) { splitted := strings.Split(str, Separator) - if len(splitted) != 5 && len(splitted) != 6 { + splen := len(splitted) + // search for rightmost -vX + offset := 0 + for ((4+offset < splen) && (splitted[4+offset][0] != 'v')) { + offset += 1 + } + if ((splen < 5+offset) || (4+offset == splen) || (splitted[4+offset][0] != 'v') || (splen > 6+offset)) { return ClusterStack{}, ErrInvalidFormat } - + var name string + if offset != 0 { + name = strings.Join(splitted[1:2+offset], Separator) + } else { + name = splitted[1] + } clusterStack := ClusterStack{ Provider: splitted[0], - Name: splitted[1], + Name: name, } if clusterStack.Provider == "" { @@ -78,18 +89,22 @@ func NewFromClusterClassProperties(str string) (ClusterStack, error) { var err error - clusterStack.KubernetesVersion, err = kubernetesversion.New(splitted[2], splitted[3]) + clusterStack.KubernetesVersion, err = kubernetesversion.New(splitted[2+offset], splitted[3+offset]) if err != nil { - return ClusterStack{}, fmt.Errorf("failed to create Kubernetes version from %s-%s: %w", splitted[2], splitted[3], err) + return ClusterStack{}, fmt.Errorf("failed to create Kubernetes version from %s-%s: %w", + splitted[2+offset], splitted[3+offset], err) } var versionString string - if len(splitted) == 5 { + if splen-offset == 5 { // e.g. myprovider-myclusterstack-1-26-v1 - versionString = splitted[4] - } else if len(splitted) == 6 { + versionString = splitted[4+offset] + } else if splen-offset == 6 { // e.g. myprovider-myclusterstack-1-26-v1-alpha.0 - versionString = strings.Join(splitted[4:6], Separator) + versionString = strings.Join(splitted[4+offset:6+offset], Separator) + } else { + // this should be impossible + panic("The impossible error") } // version string like v1-alpha.0 @@ -107,37 +122,44 @@ func NewFromClusterClassProperties(str string) (ClusterStack, error) { // e.g. - "docker-ferrol-1-27-v1", "docker-ferrol-1-27-v1-alpha-1", etc. func NewFromClusterStackReleaseProperties(str string) (ClusterStack, error) { splitted := strings.Split(str, Separator) - if len(splitted) != 5 && len(splitted) != 7 { + splen := len(splitted) + // search for rightmost -vX + offset := 0 + for ((4+offset < splen) && (splitted[4+offset][0] != 'v')) { + offset += 1 + } + if ((splen < 5+offset) || (4+offset == splen) || (splitted[4+offset][0] != 'v') || (splen > 7+offset)) { return ClusterStack{}, ErrInvalidFormat } - + var name string + if offset != 0 { + name = strings.Join(splitted[1:2+offset], Separator) + } else { + name = splitted[1] + } clusterStack := ClusterStack{ Provider: splitted[0], - Name: splitted[1], - } - - if clusterStack.Provider == "" { - return ClusterStack{}, ErrInvalidProvider - } - - if clusterStack.Name == "" { - return ClusterStack{}, ErrInvalidName + Name: name, } var err error - clusterStack.KubernetesVersion, err = kubernetesversion.New(splitted[2], splitted[3]) + clusterStack.KubernetesVersion, err = kubernetesversion.New(splitted[2+offset], splitted[3+offset]) if err != nil { - return ClusterStack{}, fmt.Errorf("failed to create Kubernetes version from %s-%s: %w", splitted[2], splitted[3], err) + return ClusterStack{}, fmt.Errorf("failed to create Kubernetes version from %s-%s: %w", + splitted[2+offset], splitted[3+offset], err) } var versionString string - if len(splitted) == 5 { + if splen-offset == 5 { // e.g. myprovider-myclusterstack-1-26-v1 - versionString = splitted[4] - } else if len(splitted) == 7 { + versionString = splitted[4+offset] + } else if splen-offset == 6 { + // e.g. myprovider-myclusterstack-1-26-v1-alpha + versionString = strings.Join(splitted[4+offset:6+offset], Separator) + } else if splen-offset == 7 { // e.g. myprovider-myclusterstack-1-26-v1-alpha-0 - versionString = strings.Join(splitted[4:7], Separator) + versionString = strings.Join(splitted[4+offset:7+offset], Separator) } // version string like v1-alpha-0