diff --git a/cmd/krel/cmd/obs_specs.go b/cmd/krel/cmd/obs_specs.go index f3e2d8bab25..99b2002b711 100644 --- a/cmd/krel/cmd/obs_specs.go +++ b/cmd/krel/cmd/obs_specs.go @@ -22,115 +22,99 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "k8s.io/release/pkg/obs/options" "k8s.io/release/pkg/obs/specs" ) -var obsOpts = options.New() +var specsOpts = specs.DefaultOptions() // obsSpecsCmd represents the subcommand for `krel obs specs` var obsSpecsCmd = &cobra.Command{ Use: "specs", - Short: "generate specs and binary archives", + Short: "generate specs and artifacts archive", SilenceUsage: true, SilenceErrors: true, RunE: func(cmd *cobra.Command, args []string) error { - return runGenerateOBSSpecs(obsOpts) + return runGenerateOBSSpecs(specsOpts) }, } func init() { obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.Package, + &specsOpts.Package, "package", - obsOpts.Package, + specsOpts.Package, "package to create specs and archives for", ) obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.Channel, - "channel", - obsOpts.Channel, - "channel to create specs for", - ) - - obsSpecsCmd.PersistentFlags().StringSliceVar( - &obsOpts.Architectures, - "arch", - obsOpts.Architectures, - "architectures to download binaries for when creating the archive", - ) - - obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.Version, + &specsOpts.Version, "version", - obsOpts.Version, + specsOpts.Version, "package version", ) obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.Revision, + &specsOpts.Revision, "revision", - obsOpts.Revision, + specsOpts.Revision, "package revision", ) obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.PackageSourceBase, + &specsOpts.Channel, + "channel", + specsOpts.Channel, + "channel to create specs for", + ) + + obsSpecsCmd.PersistentFlags().StringSliceVar( + &specsOpts.Architectures, + "architectures", + specsOpts.Architectures, + "architectures to download binaries for when creating the archive", + ) + + obsSpecsCmd.PersistentFlags().StringVar( + &specsOpts.PackageSourceBase, "package-source", - obsOpts.PackageSourceBase, + specsOpts.PackageSourceBase, "source to download artifacts for package", ) obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.SpecTemplatePath, + &specsOpts.SpecTemplatePath, "template-dir", - obsOpts.SpecTemplatePath, + specsOpts.SpecTemplatePath, "template directory containing spec files", ) obsSpecsCmd.PersistentFlags().StringVar( - &obsOpts.SpecOutputPath, + &specsOpts.SpecOutputPath, "output", - obsOpts.SpecOutputPath, + specsOpts.SpecOutputPath, "output directory to store specs and archives", ) obsSpecsCmd.PersistentFlags().BoolVar( - &obsOpts.SpecOnly, + &specsOpts.SpecOnly, "spec-only", - obsOpts.SpecOnly, + specsOpts.SpecOnly, "only create specs without downloading binaries and creating archives", ) obsCmd.AddCommand(obsSpecsCmd) } -func runGenerateOBSSpecs(opts *options.Options) (err error) { - logrus.Debugf("Using options: %+v", opts) +func runGenerateOBSSpecs(opts *specs.Options) (err error) { + logrus.Debugf("Using options: %s", opts.String()) if err := opts.Validate(); err != nil { return fmt.Errorf("running krel obs: %w", err) } - client := specs.New(opts) - - pkgDef, err := client.ConstructPackageDefinition() - if err != nil { - return fmt.Errorf("running krel obs: %w", err) - } - - if err = client.BuildSpecs(pkgDef, opts.SpecOnly); err != nil { + if err := specs.New(opts).Run(); err != nil { return fmt.Errorf("running krel obs: %w", err) } - if !opts.SpecOnly { - if err = client.BuildArtifactsArchive(pkgDef); err != nil { - return fmt.Errorf("running krel obs: %w", err) - } - } - - logrus.Infof("krel obs done, files available in %s", opts.SpecOutputPath) - return nil } diff --git a/go.mod b/go.mod index 486c6781d04..43f8278201a 100644 --- a/go.mod +++ b/go.mod @@ -40,8 +40,8 @@ require ( sigs.k8s.io/bom v0.5.2-0.20230519223618-1ebaa9ce375f sigs.k8s.io/mdtoc v1.1.0 sigs.k8s.io/promo-tools/v3 v3.6.0 - sigs.k8s.io/release-sdk v0.10.2-0.20230524114850-ade46299d040 - sigs.k8s.io/release-utils v0.7.4 + sigs.k8s.io/release-sdk v0.10.2-0.20230526102852-a20f93c51631 + sigs.k8s.io/release-utils v0.7.5-0.20230526101452-f7d5a1fbd5ad sigs.k8s.io/yaml v1.3.0 sigs.k8s.io/zeitgeist v0.4.1 ) diff --git a/go.sum b/go.sum index f10728ea4cd..4790426cf93 100644 --- a/go.sum +++ b/go.sum @@ -1611,10 +1611,10 @@ sigs.k8s.io/mdtoc v1.1.0 h1:q3YtqYzmC2e0hgLXRIOm7/QLuPux1CX3ZHCwlbABxZo= sigs.k8s.io/mdtoc v1.1.0/go.mod h1:QZLVEdHH2iNIR4uHAZyvFRtjloHgVItk8lo/mzCtq3w= sigs.k8s.io/promo-tools/v3 v3.6.0 h1:C2L08ezrWm1aZI8Emd3iZPZQserLPRgzuqQVxvI0PUI= sigs.k8s.io/promo-tools/v3 v3.6.0/go.mod h1:XJ3jy0hJYs+hWKt8XsLHFzGQV8PUtvllvbxjN/E5RXI= -sigs.k8s.io/release-sdk v0.10.2-0.20230524114850-ade46299d040 h1:W2x+/jyBHB8PDBBozU8FHfCPRQ44/zaIBNheapdvjS0= -sigs.k8s.io/release-sdk v0.10.2-0.20230524114850-ade46299d040/go.mod h1:Go3wt8dMyJOayvZjANFfylamvCauGO4syI73kVHPGTw= -sigs.k8s.io/release-utils v0.7.4 h1:17LmJrydpUloTCtaoWj95uKlcrUp4h2A9Sa+ZL+lV9w= -sigs.k8s.io/release-utils v0.7.4/go.mod h1:JEt2QPHItd5Pg2UKLAU8PEaSlF4bUjCZimpxFDgymVU= +sigs.k8s.io/release-sdk v0.10.2-0.20230526102852-a20f93c51631 h1:Nlfp/k1YPaEGjwrK+y3zlnIQcjSmb4g6LH9oXGKeZ+I= +sigs.k8s.io/release-sdk v0.10.2-0.20230526102852-a20f93c51631/go.mod h1:Go3wt8dMyJOayvZjANFfylamvCauGO4syI73kVHPGTw= +sigs.k8s.io/release-utils v0.7.5-0.20230526101452-f7d5a1fbd5ad h1:VIkuEZXIvpWG2kJJf4iphoN79zcINjJ6waUy8gAihtw= +sigs.k8s.io/release-utils v0.7.5-0.20230526101452-f7d5a1fbd5ad/go.mod h1:fsXUqmj1ekJ5sfkmfP3J0x9gh96NwVucQO6sG9STMLM= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/pkg/obs/consts/consts.go b/pkg/obs/consts/consts.go new file mode 100644 index 00000000000..0bfccedcdcc --- /dev/null +++ b/pkg/obs/consts/consts.go @@ -0,0 +1,97 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package consts + +import "github.com/sirupsen/logrus" + +const ( + PackageCRITools string = "cri-tools" + PackageKubeadm string = "kubeadm" + PackageKubectl string = "kubectl" + PackageKubelet string = "kubelet" + PackageKubernetesCNI string = "kubernetes-cni" +) + +const ( + ChannelTypeRelease string = "release" + ChannelTypePrerelease string = "prerelease" + ChannelTypeNightly string = "nightly" +) + +const ( + ArchitectureAMD64 string = "amd64" + ArchitectureARM64 string = "arm64" + ArchitecturePPC64 string = "ppc64le" + ArchitectureS390X string = "s390x" +) + +var ( + SupportedChannels = []string{ + ChannelTypeRelease, + ChannelTypePrerelease, + ChannelTypeNightly, + } + + SupportedArchitectures = []string{ + ArchitectureAMD64, + ArchitectureARM64, + ArchitecturePPC64, + ArchitectureS390X, + } +) + +const ( + DefaultReleaseDownloadLinkBase = "gs://kubernetes-release/release" + DefaultRevision = "0" +) + +func IsSupported(field string, input, expected []string) bool { + notSupported := []string{} + + supported := false + for _, i := range input { + supported = false + for _, j := range expected { + if i == j { + supported = true + break + } + } + + if !supported { + notSupported = append(notSupported, i) + } + } + + if len(notSupported) > 0 { + logrus.Infof( + "Flag %s has an unsupported option: %v", field, notSupported, + ) + return false + } + + return true +} + +func IsCoreKubernetesPackage(packageName string) bool { + switch packageName { + case PackageKubeadm, PackageKubectl, PackageKubelet: + return true + default: + return false + } +} diff --git a/pkg/obs/options/options.go b/pkg/obs/options/options.go deleted file mode 100644 index b99a5373383..00000000000 --- a/pkg/obs/options/options.go +++ /dev/null @@ -1,181 +0,0 @@ -/* -Copyright 2023 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package options - -import ( - "errors" - "os" - "path/filepath" - - "github.com/sirupsen/logrus" - - "sigs.k8s.io/release-utils/util" -) - -// Options defines options for the package building process. -type Options struct { - // Package is name of the package to build. - Package string - - // Version is the package version. - // For kubelet, kubeadm, kubectl, this is Kubernetes version. - // For cri-tools, this is cri-tools version. - // For kubernetes-cni, this is cni-plugins version. - Version string - - // Revision is the package revision. - Revision string - - // Channel is a release Channel that we're building packages for. - // This can be one of: release, prerelease, nightly. - Channel string - - // Architectures to download binaries for. - // This can be one of: amd64, arm64, ppc64le, s390x. - Architectures []string - - // PackageSourceBase is the base URL to download artifacts from. - PackageSourceBase string - - // SpecTemplatePath is a path to a directory with spec template files. - SpecTemplatePath string - - // SpecOutputPath is a path to a directory where to save spec files and archives. - SpecOutputPath string - - // SpecOnly generates only spec files. - SpecOnly bool -} - -const ( - PackageCRITools string = "cri-tools" - PackageKubeadm string = "kubeadm" - PackageKubectl string = "kubectl" - PackageKubelet string = "kubelet" - PackageKubernetesCNI string = "kubernetes-cni" -) - -const ( - ChannelTypeRelease string = "release" - ChannelTypePrerelease string = "prerelease" - ChannelTypeNightly string = "nightly" -) - -const ( - ArchitectureAMD64 string = "amd64" - ArchitectureARM64 string = "arm64" - ArchitecturePPC64 string = "ppc64le" - ArchitectureS390X string = "s390x" -) - -var ( - supportedPackages = []string{ - PackageCRITools, - PackageKubeadm, - PackageKubectl, - PackageKubelet, - PackageKubernetesCNI, - } - supportedChannels = []string{ - ChannelTypeRelease, - ChannelTypePrerelease, - ChannelTypeNightly, - } - supportedArchitectures = []string{ - ArchitectureAMD64, - ArchitectureARM64, - ArchitecturePPC64, - ArchitectureS390X, - } - // TODO(xmudrii): Remove this. - defaultTemplateDir = filepath.Join(templateRootDir, "latest") -) - -const ( - templateRootDir = "cmd/krel/templates/" - - DefaultReleaseDownloadLinkBase = "gs://kubernetes-release/release" - DefaultCNIDownloadLinkBase = "gs://k8s-artifacts-cni/release" - DefaultCRIToolsDownloadLinkBase = "gs://k8s-artifacts-cri-tools/release" - - defaultChannel = ChannelTypeRelease - defaultRevision = "0" -) - -func New() *Options { - return &Options{ - Revision: defaultRevision, - Channel: defaultChannel, - Architectures: supportedArchitectures, - SpecTemplatePath: defaultTemplateDir, - } -} - -// Validate verifies if all set options are valid -func (o *Options) Validate() error { - if ok := isSupported("package", []string{o.Package}, supportedPackages); !ok { - return errors.New("selected package is not supported") - } - if ok := isSupported("channel", []string{o.Channel}, supportedChannels); !ok { - return errors.New("selected channel is not supported") - } - if ok := isSupported("architectures", o.Architectures, supportedArchitectures); !ok { - return errors.New("architectures selection is not supported") - } - if o.Revision == "" { - return errors.New("revision is required") - } - - if o.SpecOutputPath != "" { - if _, err := os.Stat(o.SpecOutputPath); err != nil { - return errors.New("output dir doesn't exist") - } - } - - // Replace the "+" with a "-" to make it semver-compliant - o.Version = util.TrimTagPrefix(o.Version) - - return nil -} - -func isSupported(field string, input, expected []string) bool { - notSupported := []string{} - - supported := false - for _, i := range input { - supported = false - for _, j := range expected { - if i == j { - supported = true - break - } - } - - if !supported { - notSupported = append(notSupported, i) - } - } - - if len(notSupported) > 0 { - logrus.Infof( - "Flag %s has an unsupported option: %v", field, notSupported, - ) - return false - } - - return true -} diff --git a/pkg/obs/specs/archive.go b/pkg/obs/specs/archive.go index 1d24169419d..ebfb32f7609 100644 --- a/pkg/obs/specs/archive.go +++ b/pkg/obs/specs/archive.go @@ -27,14 +27,20 @@ import ( "github.com/sirupsen/logrus" - "k8s.io/release/pkg/obs/options" - "sigs.k8s.io/release-sdk/object" - "sigs.k8s.io/release-utils/tar" + "k8s.io/release/pkg/obs/consts" ) -// BuildArtifactsArchive downloads and archives artifacts from the given package source for all selected architectures. -// This archive is used as a source for artifacts by OpenBuildService when building the package. -func (c *Client) BuildArtifactsArchive(pkgDef *PackageDefinition) error { +var obsArchitectures = map[string]string{ + "amd64": "x86_64", + "arm64": "aarch64", + "ppc64le": "ppc64le", + "s390x": "s390x", +} + +// BuildArtifactsArchive downloads and archives artifacts from the given +// package source for all selected architectures. This archive is used as +// a source for artifacts by OpenBuildService when building the package. +func (s *Specs) BuildArtifactsArchive(pkgDef *PackageDefinition) error { if pkgDef == nil { return errors.New("package definition cannot be nil") } @@ -42,12 +48,13 @@ func (c *Client) BuildArtifactsArchive(pkgDef *PackageDefinition) error { logrus.Infof("Downloading artifacts for %s %s...", pkgDef.Name, pkgDef.Version) for _, pkgVar := range pkgDef.Variations { - logrus.Infof("Downloading %s %s (%s)...", pkgDef.Name, pkgDef.Version, pkgVar.Architecture) + arch := obsArchitectures[pkgVar.Architecture] + logrus.Infof("Downloading %s %s (%s)...", pkgDef.Name, pkgDef.Version, arch) - dlRootPath := filepath.Join(pkgDef.SpecOutputPath, pkgDef.Name, pkgVar.Architecture) - err := os.MkdirAll(dlRootPath, os.FileMode(0o755)) + dlRootPath := filepath.Join(pkgDef.SpecOutputPath, pkgDef.Name, arch) + err := s.impl.MkdirAll(dlRootPath, os.FileMode(0o755)) if err != nil { - if !os.IsExist(err) { + if !s.impl.IsExist(err) { return fmt.Errorf("creating directory to download %s: %w", pkgDef.Name, err) } } @@ -57,21 +64,21 @@ func (c *Client) BuildArtifactsArchive(pkgDef *PackageDefinition) error { var dlPath string var dlTarGz bool switch pkgDef.Name { - case options.PackageKubernetesCNI: + case consts.PackageKubernetesCNI: dlPath = filepath.Join(dlRootPath, "kubernetes-cni.tar.gz") dlTarGz = true - case options.PackageCRITools: + case consts.PackageCRITools: dlPath = filepath.Join(dlRootPath, "cri-tools.tar.gz") dlTarGz = true default: dlPath = filepath.Join(dlRootPath, pkgDef.Name) } - if err := c.downloadArtifact(pkgVar.Source, dlPath, dlTarGz); err != nil { + if err := s.DownloadArtifact(pkgVar.Source, dlPath, dlTarGz); err != nil { return fmt.Errorf("downloading artifacts: %w", err) } - logrus.Infof("Successfully downloaded %s %s (%s).", pkgDef.Name, pkgDef.Version, pkgVar.Architecture) + logrus.Infof("Successfully downloaded %s %s (%s).", pkgDef.Name, pkgDef.Version, arch) } logrus.Infof("Download completed successfully for %s %s.", pkgDef.Name, pkgDef.Version) @@ -80,10 +87,10 @@ func (c *Client) BuildArtifactsArchive(pkgDef *PackageDefinition) error { archiveSrc := filepath.Join(pkgDef.SpecOutputPath, pkgDef.Name) archiveDst := filepath.Join(pkgDef.SpecOutputPath, fmt.Sprintf("%s_%s.orig.tar.gz", pkgDef.Name, pkgDef.Version)) - if err := tar.Compress(archiveDst, archiveSrc); err != nil { + if err := s.impl.Compress(archiveDst, archiveSrc); err != nil { return fmt.Errorf("creating archive: %w", err) } - if err := os.RemoveAll(archiveSrc); err != nil { + if err := s.impl.RemoveAll(archiveSrc); err != nil { return fmt.Errorf("cleaning up archive source: %w", err) } @@ -92,25 +99,24 @@ func (c *Client) BuildArtifactsArchive(pkgDef *PackageDefinition) error { return nil } -// downloadArtifact is a wrapper function that runs appropriate download function depending if the package source URL scheme -// is gs:// or https:// -func (c *Client) downloadArtifact(sourcePath, destPath string, extractTgz bool) error { +// DownloadArtifact is a wrapper function that runs appropriate download +// function depending if the package source URL scheme is gs:// or https:// +func (s *Specs) DownloadArtifact(sourcePath, destPath string, extractTgz bool) error { if strings.HasPrefix(sourcePath, "gs://") { - return c.downloadArtifactFromGCS(sourcePath, destPath, extractTgz) + return s.DownloadArtifactFromGCS(sourcePath, destPath, extractTgz) } - return c.downloadArtifactFromURL(sourcePath, destPath, extractTgz) + return s.DownloadArtifactFromURL(sourcePath, destPath, extractTgz) } -// downloadArtifactFromGCS downloads the artifact from the given GCS bucket. -func (c *Client) downloadArtifactFromGCS(sourcePath, destPath string, extractTgz bool) error { - gcsClient := object.NewGCS() - if err := gcsClient.CopyToLocal(sourcePath, destPath); err != nil { +// DownloadArtifactFromGCS downloads the artifact from the given GCS bucket. +func (s *Specs) DownloadArtifactFromGCS(sourcePath, destPath string, extractTgz bool) error { + if err := s.impl.GCSCopyToLocal(sourcePath, destPath); err != nil { return fmt.Errorf("copying file to archive: %w", err) } if extractTgz { - if err := tar.Extract(destPath, filepath.Dir(destPath)); err != nil { + if err := s.impl.Extract(destPath, filepath.Dir(destPath)); err != nil { return fmt.Errorf("extracting .tar.gz archive: %w", err) } } @@ -119,14 +125,14 @@ func (c *Client) downloadArtifactFromGCS(sourcePath, destPath string, extractTgz } // downloadArtifactFromGCS downloads the artifact from the given URL. -func (c *Client) downloadArtifactFromURL(downloadURL, destPath string, extractTgz bool) error { - out, err := os.Create(destPath) +func (s *Specs) DownloadArtifactFromURL(downloadURL, destPath string, extractTgz bool) error { + out, err := s.impl.CreateFile(destPath) if err != nil { return fmt.Errorf("creating download destination file: %w", err) } defer out.Close() - resp, err := c.impl.GetRequest(downloadURL) + resp, err := s.impl.GetRequest(downloadURL) if err != nil { return fmt.Errorf("downloading artifact: %w", err) } @@ -141,10 +147,10 @@ func (c *Client) downloadArtifactFromURL(downloadURL, destPath string, extractTg } if extractTgz { - if err := tar.Extract(destPath, filepath.Dir(destPath)); err != nil { + if err := s.impl.Extract(destPath, filepath.Dir(destPath)); err != nil { return fmt.Errorf("extracting .tar.gz archive: %w", err) } - if err := os.Remove(destPath); err != nil { + if err := s.impl.RemoveFile(destPath); err != nil { return fmt.Errorf("removing extracted archive: %w", err) } } diff --git a/pkg/obs/specs/helpers.go b/pkg/obs/specs/helpers.go index 91f3815ff65..49a109c1018 100644 --- a/pkg/obs/specs/helpers.go +++ b/pkg/obs/specs/helpers.go @@ -22,22 +22,14 @@ import ( "github.com/sirupsen/logrus" - "k8s.io/release/pkg/obs/options" + "k8s.io/release/pkg/obs/consts" "k8s.io/release/pkg/release" "sigs.k8s.io/release-utils/util" ) -func isCoreKubernetesPackage(packageName string) bool { - switch packageName { - case options.PackageKubeadm, options.PackageKubectl, options.PackageKubelet: - return true - default: - return false - } -} - -func getKubernetesChannelForVersion(kubernetesVersion string) (string, error) { - kubeSemver, err := util.TagStringToSemver(kubernetesVersion) +// GetKubernetesChannelForVersion returns channel for the given Kubernetes version. +func (s *Specs) GetKubernetesChannelForVersion(kubernetesVersion string) (string, error) { + kubeSemver, err := s.impl.TagStringToSemver(kubernetesVersion) if err != nil { return "", fmt.Errorf("user-supplied kubernetes version is not valid semver: %w", err) } @@ -48,46 +40,49 @@ func getKubernetesChannelForVersion(kubernetesVersion string) (string, error) { switch { case len(kubeVersionParts) > 4: logrus.Info("User-supplied Kubernetes version is a CI version, using nightly channel") - return options.ChannelTypeNightly, nil + return consts.ChannelTypeNightly, nil case len(kubeVersionParts) == 4: logrus.Info("User-supplied Kubernetes version is a pre-release version, using testing channel") - return options.ChannelTypePrerelease, nil + return consts.ChannelTypePrerelease, nil default: logrus.Info("User-supplied Kubernetes version is a release version, using release channel") - return options.ChannelTypeRelease, nil + return consts.ChannelTypeRelease, nil } } -// getKuebrnetesVersion is used to determine the Kubernetes version based on -// provided channel. If Kubernetes version is provided by the user, this -// function does nothing. -func (c *Client) getKubernetesVersionForChannel(channel string) (string, error) { +// GetKubernetesVersionForChannel is used to determine the Kubernetes version +// based on the provided channel. +func (s *Specs) GetKubernetesVersionForChannel(channel string) (string, error) { switch channel { - case options.ChannelTypePrerelease: - return c.impl.GetKubeVersion(release.VersionTypeStablePreRelease) - case options.ChannelTypeNightly: - return c.impl.GetKubeVersion(release.VersionTypeCILatestCross) + case consts.ChannelTypePrerelease: + return s.impl.GetKubeVersion(release.VersionTypeStablePreRelease) + case consts.ChannelTypeNightly: + return s.impl.GetKubeVersion(release.VersionTypeCILatestCross) default: - return c.impl.GetKubeVersion(release.VersionTypeStable) + return s.impl.GetKubeVersion(release.VersionTypeStable) } } -func (c *Client) getKubernetesDownloadLink(channel, baseURL, name, version, arch string) func() (string, error) { +// GetKubernetesDownloadLink gets the download link for Kubernetes packages +// based on given options. +func (s *Specs) GetKubernetesDownloadLink(channel, baseURL, name, version, arch string) func() (string, error) { switch channel { - case options.ChannelTypeNightly: + case consts.ChannelTypeNightly: return func() (string, error) { - return c.getKubernetesCIDownloadLink(baseURL, name, version, arch) + return s.GetKubernetesCIDownloadLink(baseURL, name, version, arch) } default: return func() (string, error) { - return getKubernetesReleaseDownloadLink(baseURL, name, version, arch), nil + return s.GetKubernetesReleaseDownloadLink(baseURL, name, version, arch), nil } } } -func getKubernetesReleaseDownloadLink(baseURL, name, version, arch string) string { +// GetKubernetesReleaseDownloadLink gets the download link for release version +// of Kubernetes. +func (s *Specs) GetKubernetesReleaseDownloadLink(baseURL, name, version, arch string) string { if baseURL == "" { - baseURL = options.DefaultReleaseDownloadLinkBase + baseURL = consts.DefaultReleaseDownloadLinkBase } return fmt.Sprintf( @@ -99,14 +94,16 @@ func getKubernetesReleaseDownloadLink(baseURL, name, version, arch string) strin ) } -func (c *Client) getKubernetesCIDownloadLink(baseURL, name, version, arch string) (string, error) { +// GetKubernetesCIDownloadLink gets the download link for CI version of +// Kubernetes. +func (s *Specs) GetKubernetesCIDownloadLink(baseURL, name, version, arch string) (string, error) { if baseURL == "" { - baseURL = options.DefaultReleaseDownloadLinkBase + baseURL = consts.DefaultReleaseDownloadLinkBase } if version == "" { var err error - version, err = c.impl.GetKubeVersion(release.VersionTypeCILatestCross) + version, err = s.impl.GetKubeVersion(release.VersionTypeCILatestCross) if err != nil { return "", err } diff --git a/pkg/obs/specs/impl.go b/pkg/obs/specs/impl.go new file mode 100644 index 00000000000..1c059232ebe --- /dev/null +++ b/pkg/obs/specs/impl.go @@ -0,0 +1,126 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package specs + +import ( + "net/http" + "os" + "path/filepath" + "regexp" + "time" + + "github.com/blang/semver/v4" + "k8s.io/release/pkg/obs/metadata" + "k8s.io/release/pkg/release" + "sigs.k8s.io/release-sdk/object" + khttp "sigs.k8s.io/release-utils/http" + "sigs.k8s.io/release-utils/tar" + "sigs.k8s.io/release-utils/util" +) + +type defaultImpl struct{} + +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate +//counterfeiter:generate . impl +//go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate/boilerplate.generatego.txt specsfakes/fake_impl.go > specsfakes/_fake_impl.go && mv specsfakes/_fake_impl.go specsfakes/fake_impl.go" +type impl interface { + GetKubeVersion(versionType release.VersionType) (string, error) + GetRequest(url string) (*http.Response, error) + CreateFile(name string) (*os.File, error) + WriteFile(name string, data []byte, perm os.FileMode) error + Mkdir(path string, perm os.FileMode) error + MkdirAll(path string, perm os.FileMode) error + RemoveFile(name string) error + RemoveAll(path string) error + IsExist(err error) bool + Stat(name string) (os.FileInfo, error) + Walk(root string, fn filepath.WalkFunc) error + Compress(tarFilePath, tarContentsPath string, excludes ...*regexp.Regexp) error + Extract(tarFilePath, destinationPath string) error + GCSCopyToLocal(gcsPath, dst string) error + TagStringToSemver(tag string) (semver.Version, error) + TrimTagPrefix(tag string) string + LoadPackageMetadata(path string) (metadata.PackageMetadataList, error) +} + +func (d *defaultImpl) GetKubeVersion(versionType release.VersionType) (string, error) { + return release.NewVersion().GetKubeVersion(versionType) +} + +func (d *defaultImpl) GetRequest(url string) (*http.Response, error) { + return khttp.NewAgent().WithTimeout(3 * time.Minute).GetRequest(url) +} + +func (d *defaultImpl) CreateFile(name string) (*os.File, error) { + return os.Create(name) +} + +func (d *defaultImpl) WriteFile(name string, data []byte, perm os.FileMode) error { + return os.WriteFile(name, data, perm) +} + +func (d *defaultImpl) Mkdir(path string, perm os.FileMode) error { + return os.Mkdir(path, perm) +} + +func (d *defaultImpl) MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} + +func (d *defaultImpl) IsExist(err error) bool { + return os.IsExist(err) +} + +func (d *defaultImpl) Stat(name string) (os.FileInfo, error) { + return os.Stat(name) +} + +func (d *defaultImpl) Walk(root string, fn filepath.WalkFunc) error { + return filepath.Walk(root, fn) +} + +func (d *defaultImpl) RemoveFile(name string) error { + return os.Remove(name) +} + +func (d *defaultImpl) RemoveAll(path string) error { + return os.RemoveAll(path) +} + +func (d *defaultImpl) Compress(tarFilePath, tarContentsPath string, excludes ...*regexp.Regexp) error { + return tar.CompressWithoutPreservingPath(tarFilePath, tarContentsPath, excludes...) +} + +func (d *defaultImpl) Extract(tarFilePath, destinationPath string) error { + return tar.Extract(tarFilePath, destinationPath) +} + +func (d *defaultImpl) GCSCopyToLocal(gcsPath, dst string) error { + return object.NewGCS().CopyToLocal(gcsPath, dst) +} + +func (d *defaultImpl) TagStringToSemver(tag string) (semver.Version, error) { + return util.TagStringToSemver(tag) +} + +func (d *defaultImpl) TrimTagPrefix(tag string) string { + return util.TrimTagPrefix(tag) +} + +func (d *defaultImpl) LoadPackageMetadata(path string) (metadata.PackageMetadataList, error) { + return metadata.LoadPackageMetadata(path) +} diff --git a/pkg/obs/specs/pkgdef.go b/pkg/obs/specs/pkgdef.go new file mode 100644 index 00000000000..8c0e57184fb --- /dev/null +++ b/pkg/obs/specs/pkgdef.go @@ -0,0 +1,189 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package specs + +import ( + "bytes" + "fmt" + "path/filepath" + "strings" + "text/template" + + "github.com/blang/semver/v4" + "github.com/sirupsen/logrus" + + "k8s.io/release/pkg/obs/consts" + "k8s.io/release/pkg/obs/metadata" +) + +// PackageDefinition represents a concrete package and stores package's name, +// version, and metadata. +type PackageDefinition struct { + Name string + Version string + Revision string + Channel string + Metadata *metadata.PackageMetadata + Variations []PackageVariation + + SpecTemplatePath string + SpecOutputPath string +} + +// PackageVariation is a variation of the same package. Variation currently +// represents a different architecture and source for the given architecture. +type PackageVariation struct { + Architecture string + Source string +} + +// ConstructPackageDefinition creates a new instance of PackageDefinition based +// on provided options. +func (s *Specs) ConstructPackageDefinition() (*PackageDefinition, error) { + logrus.Infof("Constructing package definition for %s %s...", s.options.Package, s.options.Version) + + var err error + + pkgDef := &PackageDefinition{ + Name: s.options.Package, + Version: s.options.Version, + Revision: s.options.Revision, + Channel: s.options.Channel, + Variations: []PackageVariation{}, + + SpecTemplatePath: s.options.SpecTemplatePath, + SpecOutputPath: s.options.SpecOutputPath, + } + + logrus.Infof("Writing output to %s", pkgDef.SpecOutputPath) + + // If Kubernetes version is provided, ensure that it is correct and determine the channel based on it. + // Otherwise, try to automatically determine the Kubernetes version based on provided channel. + if consts.IsCoreKubernetesPackage(pkgDef.Name) && pkgDef.Version != "" { + pkgDef.Channel, err = s.GetKubernetesChannelForVersion(pkgDef.Version) + if err != nil { + return nil, fmt.Errorf("getting kubernetes channel: %w", err) + } + } else if consts.IsCoreKubernetesPackage(pkgDef.Name) && pkgDef.Version == "" { + pkgDef.Version, err = s.GetKubernetesVersionForChannel(pkgDef.Channel) + if err != nil { + return nil, fmt.Errorf("getting kubernetes version: %w", err) + } + } + + pkgDef.Version = s.impl.TrimTagPrefix(pkgDef.Version) + // For cases where a CI build version of Kubernetes is retrieved, replace instances + // of "+" with "-", so that we build with a valid Debian package version. + pkgDef.Version = strings.Replace(pkgDef.Version, "+", "-", 1) + + logrus.Infof("Using %s version %s/%s", pkgDef.Name, pkgDef.Channel, pkgDef.Version) + + // Get package metadata for the given package. Metadata includes information about package source and dependencies. + pkgDef.Metadata, err = s.GetPackageMetadata(pkgDef.SpecTemplatePath, pkgDef.Name, pkgDef.Version) + if err != nil { + return nil, fmt.Errorf("getting metadata for %q: %w", pkgDef.Name, err) + } + + // Create variation of the package for each architecture. + for _, arch := range s.options.Architectures { + sourceURL, err := s.GetPackageSource(pkgDef.Metadata.SourceURLTemplate, s.options.PackageSourceBase, pkgDef.Name, pkgDef.Version, arch, pkgDef.Channel) + if err != nil { + return nil, fmt.Errorf("getting package source download link: %w", err) + } + pkgVar := PackageVariation{ + Architecture: arch, + Source: sourceURL, + } + pkgDef.Variations = append(pkgDef.Variations, pkgVar) + } + + logrus.Infof("Successfully constructed package definition for %s %s!", pkgDef.Name, pkgDef.Version) + + return pkgDef, nil +} + +// GetPackageMetadata gets metadata for the given package. +// Metadata includes information about package source and dependencies, +// and is stored in a YAML manifest. +func (s *Specs) GetPackageMetadata(templateDir, packageName, packageVersion string) (*metadata.PackageMetadata, error) { + m, err := s.impl.LoadPackageMetadata(filepath.Join(templateDir, "metadata.yaml")) + if err != nil { + return nil, fmt.Errorf("getting metadata for %s: %w", packageName, err) + } + + deps, err := s.GetMetadataWithVersionConstraint(packageName, packageVersion, m[packageName]) + if err != nil { + return nil, fmt.Errorf("parsing metadata for %s: %w", packageName, err) + } + + return deps, nil +} + +// GetMetadataWithVersionConstraint parses metadata and takes metadata that +// matches the given version constraint. +func (s *Specs) GetMetadataWithVersionConstraint(packageName, packageVersion string, constraintedMetadata []metadata.PackageMetadata) (*metadata.PackageMetadata, error) { + for _, m := range constraintedMetadata { + r, err := semver.ParseRange(m.VersionConstraint) + if err != nil { + return nil, fmt.Errorf("parsing semver range for package %s: %w", packageName, err) + } + kubeSemVer, err := s.impl.TagStringToSemver(packageVersion) + if err != nil { + return nil, fmt.Errorf("parsing package version %s: %w", packageVersion, err) + } + + if r(kubeSemVer) { + return &m, nil + } + } + + return nil, fmt.Errorf("package %s is not defined in metadata.yaml file", packageName) +} + +// GetPackageSource gets the download link for artifacts for the given package. +// This function runs template on sourceURLTemplate defined in the metadata manifest. +func (s *Specs) GetPackageSource(templateBaseURL, baseURL, packageName, packageVersion, packageArch, channel string) (string, error) { + data := struct { + BaseURL string + PackageName string + PackageVersion string + Architecture string + Channel string + }{ + BaseURL: baseURL, + PackageName: packageName, + PackageVersion: packageVersion, + Architecture: packageArch, + Channel: channel, + } + + tpl, err := template.New("").Funcs( + template.FuncMap{ + "KubernetesURL": s.GetKubernetesDownloadLink(channel, baseURL, packageName, packageVersion, packageArch), + }, + ).Parse(templateBaseURL) + if err != nil { + return "", fmt.Errorf("getting download link base: creating template: %w", err) + } + + var outBuf bytes.Buffer + if err := tpl.Execute(&outBuf, data); err != nil { + return "", fmt.Errorf("getting download link base: executing template: %w", err) + } + + return outBuf.String(), nil +} diff --git a/pkg/obs/specs/specs.go b/pkg/obs/specs/specs.go index 29ce99890b0..8e32db89fed 100644 --- a/pkg/obs/specs/specs.go +++ b/pkg/obs/specs/specs.go @@ -17,218 +17,147 @@ limitations under the License. package specs import ( - "bytes" + "errors" "fmt" - "net/http" "os" "path/filepath" - "strings" - "text/template" - "time" - "github.com/blang/semver/v4" "github.com/sirupsen/logrus" - - "k8s.io/release/pkg/obs/metadata" - "k8s.io/release/pkg/obs/options" - "k8s.io/release/pkg/release" - khttp "sigs.k8s.io/release-utils/http" + "k8s.io/release/pkg/obs/consts" "sigs.k8s.io/release-utils/util" ) -type Client struct { - options *options.Options - impl Impl -} - -func New(o *options.Options) *Client { - return &Client{ - options: o, - impl: &impl{}, - } -} - -func (c *Client) SetImpl(impl Impl) { - c.impl = impl -} - -type impl struct{} - -//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate -//counterfeiter:generate . Impl -//go:generate /usr/bin/env bash -c "cat ../../../hack/boilerplate/boilerplate.generatego.txt specsfakes/fake_impl.go > specsfakes/_fake_impl.go && mv specsfakes/_fake_impl.go specsfakes/fake_impl.go" -type Impl interface { - GetKubeVersion(versionType release.VersionType) (string, error) - GetRequest(url string) (*http.Response, error) -} +// Options defines options for generating specs and artifacts archive for +// the given package. +type Options struct { + // Package is name of the package to generate specs and artifacts archive for. + Package string + + // Version is the package version. + // For kubelet, kubeadm, kubectl, this is Kubernetes version. + // For cri-tools, this is cri-tools version. + // For kubernetes-cni, this is cni-plugins version. + Version string + + // Revision is the package revision. + Revision string + + // Architectures to download binaries for. + // This can be one of: amd64, arm64, ppc64le, s390x. + Architectures []string + + // Channel is a release Channel that we're building packages for. + // It's used to determine the Kubernetes/package version if version is not + // explicitly provided. + // This can be one of: release, prerelease, nightly. + // Omit for non-core Kubernetes packages. + Channel string + + // PackageSourceBase is the base URL to download artifacts from. + // Can be https:// or gs:// URL. + PackageSourceBase string + + // SpecTemplatePath is a path to a directory with spec template files. + SpecTemplatePath string -func (i *impl) GetKubeVersion(versionType release.VersionType) (string, error) { - return release.NewVersion().GetKubeVersion(versionType) -} + // SpecOutputPath is a path to a directory where to save spec files and + // archives. The directory must exist before running the command. + SpecOutputPath string -func (i *impl) GetRequest(url string) (*http.Response, error) { - // TODO(xmudrii): Make timeout configurable. - return khttp.NewAgent().WithTimeout(3 * time.Minute).GetRequest(url) + // SpecOnly generates only spec files without the artifacts archive. + SpecOnly bool } -// PackageDefinition represents a concrete package and stores package's name, version, and metadata. -type PackageDefinition struct { - Name string - Version string - Revision string - Channel string - Metadata *metadata.PackageMetadata - Variations []PackageVariation - - SpecTemplatePath string - SpecOutputPath string +// DefaultOptions returns a new Options instance. +func DefaultOptions() *Options { + return &Options{ + Revision: consts.DefaultRevision, + Architectures: []string{ + consts.ArchitectureAMD64, + consts.ArchitectureARM64, + consts.ArchitecturePPC64, + consts.ArchitectureS390X, + }, + } } -// PackageVariation is a variation of the same package. Variation currently represents a different architecture and -// source for the given architecture. -type PackageVariation struct { - Architecture string - Source string +// String returns a string representation for the `Options` type. +func (o *Options) String() string { + return fmt.Sprintf( + "Package: %v, Version: %s-%s, Architectures: %s, Templates: %s, Output: %q", + o.Package, o.Version, o.Revision, o.Architectures, o.SpecTemplatePath, o.SpecOutputPath, + ) } -// ConstructPackageDefinition creates a new instance of PackageDefinition based on provided options. -func (c *Client) ConstructPackageDefinition() (*PackageDefinition, error) { - logrus.Infof("Constructing package definition for %s %s...", c.options.Package, c.options.Version) - - var err error - - pkgDef := &PackageDefinition{ - Name: c.options.Package, - Version: c.options.Version, - Revision: c.options.Revision, - Channel: c.options.Channel, - Variations: []PackageVariation{}, - - SpecTemplatePath: c.options.SpecTemplatePath, - SpecOutputPath: c.options.SpecOutputPath, +// Validate verifies if all parameters in the `Options` instance are valid. +func (o *Options) Validate() error { + if _, err := os.Stat(filepath.Join(o.SpecTemplatePath, o.Package)); err != nil { + return fmt.Errorf("specs for package %s doesn't exist", o.Package) } - // Check if input and output directories exist. - if _, err := os.Stat(pkgDef.SpecTemplatePath); err != nil { - return nil, fmt.Errorf("finding package template dir: %w", err) - } - if _, err := os.Stat(pkgDef.SpecOutputPath); err != nil { - return nil, fmt.Errorf("finding package output dir: %w", err) + if o.Version == "" && o.Channel == "" { + return errors.New("one of version or channel is required") } - - logrus.Infof("Writing output to %s", pkgDef.SpecOutputPath) - - // If Kubernetes version is provided, ensure that it is correct and determine the channel based on it. - // Otherwise, try to automatically determine the Kubernetes version based on provided channel. - if isCoreKubernetesPackage(pkgDef.Name) && pkgDef.Version != "" { - pkgDef.Channel, err = getKubernetesChannelForVersion(pkgDef.Version) - if err != nil { - return nil, fmt.Errorf("getting kubernetes channel: %w", err) - } - } else if isCoreKubernetesPackage(pkgDef.Name) && pkgDef.Version == "" { - pkgDef.Version, err = c.getKubernetesVersionForChannel(pkgDef.Channel) - if err != nil { - return nil, fmt.Errorf("getting kubernetes version: %w", err) + if o.Channel != "" { + if ok := consts.IsSupported("channel", []string{o.Channel}, consts.SupportedChannels); !ok { + return errors.New("selected channel is not supported") } } - pkgDef.Version = util.TrimTagPrefix(pkgDef.Version) - // For cases where a CI build version of Kubernetes is retrieved, replace instances - // of "+" with "-", so that we build with a valid Debian package version. - pkgDef.Version = strings.Replace(pkgDef.Version, "+", "-", 1) - - logrus.Infof("Using %s version %s/%s", pkgDef.Name, pkgDef.Channel, pkgDef.Version) - - // Get package metadata for the given package. Metadata includes information about package source and dependencies. - pkgDef.Metadata, err = getPackageMetadata(pkgDef.SpecTemplatePath, pkgDef.Name, pkgDef.Version) - if err != nil { - return nil, fmt.Errorf("getting metadata for %q: %w", pkgDef.Name, err) + if o.Revision == "" { + return errors.New("revision is required") } - // Create variation of the package for each architecture. - for _, arch := range c.options.Architectures { - sourceURL, err := c.getPackageSource(pkgDef.Metadata.SourceURLTemplate, c.options.PackageSourceBase, pkgDef.Name, pkgDef.Version, arch, pkgDef.Channel) - if err != nil { - return nil, fmt.Errorf("getting package source download link: %w", err) - } - pkgVar := PackageVariation{ - Architecture: arch, - Source: sourceURL, - } - pkgDef.Variations = append(pkgDef.Variations, pkgVar) + if ok := consts.IsSupported("architectures", o.Architectures, consts.SupportedArchitectures); !ok { + return errors.New("architectures selection is not supported") } - logrus.Infof("Successfully constructed package definition for %s %s!", pkgDef.Name, pkgDef.Version) - - return pkgDef, nil -} - -// getPackageMetadata gets metadata for the given package. -// Metadata includes information about package source and dependencies, and is stored in a YAML manifest. -func getPackageMetadata(templateDir, packageName, packageVersion string) (*metadata.PackageMetadata, error) { - m, err := metadata.LoadPackageMetadata(filepath.Join(templateDir, "metadata.yaml")) - if err != nil { - return nil, fmt.Errorf("getting metadata for %s: %w", packageName, err) + if _, err := os.Stat(o.SpecTemplatePath); err != nil { + return errors.New("templates dir doesn't exist") } - - deps, err := getMetadataWithVersionConstraint(packageName, packageVersion, m[packageName]) - if err != nil { - return nil, fmt.Errorf("parsing metadata for %s: %w", packageName, err) + if _, err := os.Stat(o.SpecOutputPath); err != nil { + return errors.New("output dir doesn't exist") } - return deps, nil + // Replace the "+" with a "-" to make it semver-compliant + o.Version = util.TrimTagPrefix(o.Version) + + return nil } -// getMetadataWithVersionConstraint parses metadata and takes metadata that matches the given version constraint. -func getMetadataWithVersionConstraint(packageName, packageVersion string, constraintedMetadata []metadata.PackageMetadata) (*metadata.PackageMetadata, error) { - for _, m := range constraintedMetadata { - r, err := semver.ParseRange(m.VersionConstraint) - if err != nil { - return nil, fmt.Errorf("parsing semver range for package %s: %w", packageName, err) - } - kubeSemVer, err := util.TagStringToSemver(packageVersion) - if err != nil { - return nil, fmt.Errorf("parsing package version %s: %w", packageVersion, err) - } +type Specs struct { + options *Options + impl +} - if r(kubeSemVer) { - return &m, nil - } +func New(opts *Options) *Specs { + return &Specs{ + options: opts, + impl: &defaultImpl{}, } +} - return nil, fmt.Errorf("package %s is not defined in metadata.yaml file", packageName) +func (s *Specs) SetImpl(impl impl) { + s.impl = impl } -// getPackageSource gets the download link for artifacts for the given package. -// This function runs template on sourceURLTemplate defined in the metadata manifest. -func (c *Client) getPackageSource(templateBaseURL, baseURL, packageName, packageVersion, packageArch, channel string) (string, error) { - data := struct { - BaseURL string - PackageName string - PackageVersion string - Architecture string - Channel string - }{ - BaseURL: baseURL, - PackageName: packageName, - PackageVersion: packageVersion, - Architecture: packageArch, - Channel: channel, +func (s *Specs) Run() error { + pkgDef, err := s.ConstructPackageDefinition() + if err != nil { + return fmt.Errorf("constructing package definition: %w", err) } - tpl, err := template.New("").Funcs( - template.FuncMap{ - "KubernetesURL": c.getKubernetesDownloadLink(channel, baseURL, packageName, packageVersion, packageArch), - }, - ).Parse(templateBaseURL) - if err != nil { - return "", fmt.Errorf("getting download link base: creating template: %w", err) + if err = s.BuildSpecs(pkgDef, s.options.SpecOnly); err != nil { + return fmt.Errorf("building specs: %w", err) } - var outBuf bytes.Buffer - if err := tpl.Execute(&outBuf, data); err != nil { - return "", fmt.Errorf("getting download link base: executing template: %w", err) + if !s.options.SpecOnly { + if err = s.BuildArtifactsArchive(pkgDef); err != nil { + return fmt.Errorf("building artifacts archive: %w", err) + } + } else { + logrus.Infof("Specs only option enabled, skipping artifacts archive for %s", s.options.Package) } - return outBuf.String(), nil + return nil } diff --git a/pkg/obs/specs/specsfakes/fake_impl.go b/pkg/obs/specs/specsfakes/fake_impl.go index 437cd1986fe..c03e64a4b05 100644 --- a/pkg/obs/specs/specsfakes/fake_impl.go +++ b/pkg/obs/specs/specsfakes/fake_impl.go @@ -1,15 +1,86 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + // Code generated by counterfeiter. DO NOT EDIT. package specsfakes import ( + "io/fs" "net/http" + "os" + "path/filepath" + "regexp" "sync" - "k8s.io/release/pkg/obs/specs" + semver "github.com/blang/semver/v4" + "k8s.io/release/pkg/obs/metadata" "k8s.io/release/pkg/release" ) type FakeImpl struct { + CompressStub func(string, string, ...*regexp.Regexp) error + compressMutex sync.RWMutex + compressArgsForCall []struct { + arg1 string + arg2 string + arg3 []*regexp.Regexp + } + compressReturns struct { + result1 error + } + compressReturnsOnCall map[int]struct { + result1 error + } + CreateFileStub func(string) (*os.File, error) + createFileMutex sync.RWMutex + createFileArgsForCall []struct { + arg1 string + } + createFileReturns struct { + result1 *os.File + result2 error + } + createFileReturnsOnCall map[int]struct { + result1 *os.File + result2 error + } + ExtractStub func(string, string) error + extractMutex sync.RWMutex + extractArgsForCall []struct { + arg1 string + arg2 string + } + extractReturns struct { + result1 error + } + extractReturnsOnCall map[int]struct { + result1 error + } + GCSCopyToLocalStub func(string, string) error + gCSCopyToLocalMutex sync.RWMutex + gCSCopyToLocalArgsForCall []struct { + arg1 string + arg2 string + } + gCSCopyToLocalReturns struct { + result1 error + } + gCSCopyToLocalReturnsOnCall map[int]struct { + result1 error + } GetKubeVersionStub func(release.VersionType) (string, error) getKubeVersionMutex sync.RWMutex getKubeVersionArgsForCall []struct { @@ -36,10 +107,393 @@ type FakeImpl struct { result1 *http.Response result2 error } + IsExistStub func(error) bool + isExistMutex sync.RWMutex + isExistArgsForCall []struct { + arg1 error + } + isExistReturns struct { + result1 bool + } + isExistReturnsOnCall map[int]struct { + result1 bool + } + LoadPackageMetadataStub func(string) (metadata.PackageMetadataList, error) + loadPackageMetadataMutex sync.RWMutex + loadPackageMetadataArgsForCall []struct { + arg1 string + } + loadPackageMetadataReturns struct { + result1 metadata.PackageMetadataList + result2 error + } + loadPackageMetadataReturnsOnCall map[int]struct { + result1 metadata.PackageMetadataList + result2 error + } + MkdirStub func(string, fs.FileMode) error + mkdirMutex sync.RWMutex + mkdirArgsForCall []struct { + arg1 string + arg2 fs.FileMode + } + mkdirReturns struct { + result1 error + } + mkdirReturnsOnCall map[int]struct { + result1 error + } + MkdirAllStub func(string, fs.FileMode) error + mkdirAllMutex sync.RWMutex + mkdirAllArgsForCall []struct { + arg1 string + arg2 fs.FileMode + } + mkdirAllReturns struct { + result1 error + } + mkdirAllReturnsOnCall map[int]struct { + result1 error + } + RemoveAllStub func(string) error + removeAllMutex sync.RWMutex + removeAllArgsForCall []struct { + arg1 string + } + removeAllReturns struct { + result1 error + } + removeAllReturnsOnCall map[int]struct { + result1 error + } + RemoveFileStub func(string) error + removeFileMutex sync.RWMutex + removeFileArgsForCall []struct { + arg1 string + } + removeFileReturns struct { + result1 error + } + removeFileReturnsOnCall map[int]struct { + result1 error + } + StatStub func(string) (fs.FileInfo, error) + statMutex sync.RWMutex + statArgsForCall []struct { + arg1 string + } + statReturns struct { + result1 fs.FileInfo + result2 error + } + statReturnsOnCall map[int]struct { + result1 fs.FileInfo + result2 error + } + TagStringToSemverStub func(string) (semver.Version, error) + tagStringToSemverMutex sync.RWMutex + tagStringToSemverArgsForCall []struct { + arg1 string + } + tagStringToSemverReturns struct { + result1 semver.Version + result2 error + } + tagStringToSemverReturnsOnCall map[int]struct { + result1 semver.Version + result2 error + } + TrimTagPrefixStub func(string) string + trimTagPrefixMutex sync.RWMutex + trimTagPrefixArgsForCall []struct { + arg1 string + } + trimTagPrefixReturns struct { + result1 string + } + trimTagPrefixReturnsOnCall map[int]struct { + result1 string + } + WalkStub func(string, filepath.WalkFunc) error + walkMutex sync.RWMutex + walkArgsForCall []struct { + arg1 string + arg2 filepath.WalkFunc + } + walkReturns struct { + result1 error + } + walkReturnsOnCall map[int]struct { + result1 error + } + WriteFileStub func(string, []byte, fs.FileMode) error + writeFileMutex sync.RWMutex + writeFileArgsForCall []struct { + arg1 string + arg2 []byte + arg3 fs.FileMode + } + writeFileReturns struct { + result1 error + } + writeFileReturnsOnCall map[int]struct { + result1 error + } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } +func (fake *FakeImpl) Compress(arg1 string, arg2 string, arg3 ...*regexp.Regexp) error { + fake.compressMutex.Lock() + ret, specificReturn := fake.compressReturnsOnCall[len(fake.compressArgsForCall)] + fake.compressArgsForCall = append(fake.compressArgsForCall, struct { + arg1 string + arg2 string + arg3 []*regexp.Regexp + }{arg1, arg2, arg3}) + stub := fake.CompressStub + fakeReturns := fake.compressReturns + fake.recordInvocation("Compress", []interface{}{arg1, arg2, arg3}) + fake.compressMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3...) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) CompressCallCount() int { + fake.compressMutex.RLock() + defer fake.compressMutex.RUnlock() + return len(fake.compressArgsForCall) +} + +func (fake *FakeImpl) CompressCalls(stub func(string, string, ...*regexp.Regexp) error) { + fake.compressMutex.Lock() + defer fake.compressMutex.Unlock() + fake.CompressStub = stub +} + +func (fake *FakeImpl) CompressArgsForCall(i int) (string, string, []*regexp.Regexp) { + fake.compressMutex.RLock() + defer fake.compressMutex.RUnlock() + argsForCall := fake.compressArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeImpl) CompressReturns(result1 error) { + fake.compressMutex.Lock() + defer fake.compressMutex.Unlock() + fake.CompressStub = nil + fake.compressReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) CompressReturnsOnCall(i int, result1 error) { + fake.compressMutex.Lock() + defer fake.compressMutex.Unlock() + fake.CompressStub = nil + if fake.compressReturnsOnCall == nil { + fake.compressReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.compressReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) CreateFile(arg1 string) (*os.File, error) { + fake.createFileMutex.Lock() + ret, specificReturn := fake.createFileReturnsOnCall[len(fake.createFileArgsForCall)] + fake.createFileArgsForCall = append(fake.createFileArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.CreateFileStub + fakeReturns := fake.createFileReturns + fake.recordInvocation("CreateFile", []interface{}{arg1}) + fake.createFileMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeImpl) CreateFileCallCount() int { + fake.createFileMutex.RLock() + defer fake.createFileMutex.RUnlock() + return len(fake.createFileArgsForCall) +} + +func (fake *FakeImpl) CreateFileCalls(stub func(string) (*os.File, error)) { + fake.createFileMutex.Lock() + defer fake.createFileMutex.Unlock() + fake.CreateFileStub = stub +} + +func (fake *FakeImpl) CreateFileArgsForCall(i int) string { + fake.createFileMutex.RLock() + defer fake.createFileMutex.RUnlock() + argsForCall := fake.createFileArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) CreateFileReturns(result1 *os.File, result2 error) { + fake.createFileMutex.Lock() + defer fake.createFileMutex.Unlock() + fake.CreateFileStub = nil + fake.createFileReturns = struct { + result1 *os.File + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) CreateFileReturnsOnCall(i int, result1 *os.File, result2 error) { + fake.createFileMutex.Lock() + defer fake.createFileMutex.Unlock() + fake.CreateFileStub = nil + if fake.createFileReturnsOnCall == nil { + fake.createFileReturnsOnCall = make(map[int]struct { + result1 *os.File + result2 error + }) + } + fake.createFileReturnsOnCall[i] = struct { + result1 *os.File + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) Extract(arg1 string, arg2 string) error { + fake.extractMutex.Lock() + ret, specificReturn := fake.extractReturnsOnCall[len(fake.extractArgsForCall)] + fake.extractArgsForCall = append(fake.extractArgsForCall, struct { + arg1 string + arg2 string + }{arg1, arg2}) + stub := fake.ExtractStub + fakeReturns := fake.extractReturns + fake.recordInvocation("Extract", []interface{}{arg1, arg2}) + fake.extractMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) ExtractCallCount() int { + fake.extractMutex.RLock() + defer fake.extractMutex.RUnlock() + return len(fake.extractArgsForCall) +} + +func (fake *FakeImpl) ExtractCalls(stub func(string, string) error) { + fake.extractMutex.Lock() + defer fake.extractMutex.Unlock() + fake.ExtractStub = stub +} + +func (fake *FakeImpl) ExtractArgsForCall(i int) (string, string) { + fake.extractMutex.RLock() + defer fake.extractMutex.RUnlock() + argsForCall := fake.extractArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeImpl) ExtractReturns(result1 error) { + fake.extractMutex.Lock() + defer fake.extractMutex.Unlock() + fake.ExtractStub = nil + fake.extractReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) ExtractReturnsOnCall(i int, result1 error) { + fake.extractMutex.Lock() + defer fake.extractMutex.Unlock() + fake.ExtractStub = nil + if fake.extractReturnsOnCall == nil { + fake.extractReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.extractReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) GCSCopyToLocal(arg1 string, arg2 string) error { + fake.gCSCopyToLocalMutex.Lock() + ret, specificReturn := fake.gCSCopyToLocalReturnsOnCall[len(fake.gCSCopyToLocalArgsForCall)] + fake.gCSCopyToLocalArgsForCall = append(fake.gCSCopyToLocalArgsForCall, struct { + arg1 string + arg2 string + }{arg1, arg2}) + stub := fake.GCSCopyToLocalStub + fakeReturns := fake.gCSCopyToLocalReturns + fake.recordInvocation("GCSCopyToLocal", []interface{}{arg1, arg2}) + fake.gCSCopyToLocalMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) GCSCopyToLocalCallCount() int { + fake.gCSCopyToLocalMutex.RLock() + defer fake.gCSCopyToLocalMutex.RUnlock() + return len(fake.gCSCopyToLocalArgsForCall) +} + +func (fake *FakeImpl) GCSCopyToLocalCalls(stub func(string, string) error) { + fake.gCSCopyToLocalMutex.Lock() + defer fake.gCSCopyToLocalMutex.Unlock() + fake.GCSCopyToLocalStub = stub +} + +func (fake *FakeImpl) GCSCopyToLocalArgsForCall(i int) (string, string) { + fake.gCSCopyToLocalMutex.RLock() + defer fake.gCSCopyToLocalMutex.RUnlock() + argsForCall := fake.gCSCopyToLocalArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeImpl) GCSCopyToLocalReturns(result1 error) { + fake.gCSCopyToLocalMutex.Lock() + defer fake.gCSCopyToLocalMutex.Unlock() + fake.GCSCopyToLocalStub = nil + fake.gCSCopyToLocalReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) GCSCopyToLocalReturnsOnCall(i int, result1 error) { + fake.gCSCopyToLocalMutex.Lock() + defer fake.gCSCopyToLocalMutex.Unlock() + fake.GCSCopyToLocalStub = nil + if fake.gCSCopyToLocalReturnsOnCall == nil { + fake.gCSCopyToLocalReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.gCSCopyToLocalReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeImpl) GetKubeVersion(arg1 release.VersionType) (string, error) { fake.getKubeVersionMutex.Lock() ret, specificReturn := fake.getKubeVersionReturnsOnCall[len(fake.getKubeVersionArgsForCall)] @@ -168,13 +622,733 @@ func (fake *FakeImpl) GetRequestReturnsOnCall(i int, result1 *http.Response, res }{result1, result2} } +func (fake *FakeImpl) IsExist(arg1 error) bool { + fake.isExistMutex.Lock() + ret, specificReturn := fake.isExistReturnsOnCall[len(fake.isExistArgsForCall)] + fake.isExistArgsForCall = append(fake.isExistArgsForCall, struct { + arg1 error + }{arg1}) + stub := fake.IsExistStub + fakeReturns := fake.isExistReturns + fake.recordInvocation("IsExist", []interface{}{arg1}) + fake.isExistMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) IsExistCallCount() int { + fake.isExistMutex.RLock() + defer fake.isExistMutex.RUnlock() + return len(fake.isExistArgsForCall) +} + +func (fake *FakeImpl) IsExistCalls(stub func(error) bool) { + fake.isExistMutex.Lock() + defer fake.isExistMutex.Unlock() + fake.IsExistStub = stub +} + +func (fake *FakeImpl) IsExistArgsForCall(i int) error { + fake.isExistMutex.RLock() + defer fake.isExistMutex.RUnlock() + argsForCall := fake.isExistArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) IsExistReturns(result1 bool) { + fake.isExistMutex.Lock() + defer fake.isExistMutex.Unlock() + fake.IsExistStub = nil + fake.isExistReturns = struct { + result1 bool + }{result1} +} + +func (fake *FakeImpl) IsExistReturnsOnCall(i int, result1 bool) { + fake.isExistMutex.Lock() + defer fake.isExistMutex.Unlock() + fake.IsExistStub = nil + if fake.isExistReturnsOnCall == nil { + fake.isExistReturnsOnCall = make(map[int]struct { + result1 bool + }) + } + fake.isExistReturnsOnCall[i] = struct { + result1 bool + }{result1} +} + +func (fake *FakeImpl) LoadPackageMetadata(arg1 string) (metadata.PackageMetadataList, error) { + fake.loadPackageMetadataMutex.Lock() + ret, specificReturn := fake.loadPackageMetadataReturnsOnCall[len(fake.loadPackageMetadataArgsForCall)] + fake.loadPackageMetadataArgsForCall = append(fake.loadPackageMetadataArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.LoadPackageMetadataStub + fakeReturns := fake.loadPackageMetadataReturns + fake.recordInvocation("LoadPackageMetadata", []interface{}{arg1}) + fake.loadPackageMetadataMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeImpl) LoadPackageMetadataCallCount() int { + fake.loadPackageMetadataMutex.RLock() + defer fake.loadPackageMetadataMutex.RUnlock() + return len(fake.loadPackageMetadataArgsForCall) +} + +func (fake *FakeImpl) LoadPackageMetadataCalls(stub func(string) (metadata.PackageMetadataList, error)) { + fake.loadPackageMetadataMutex.Lock() + defer fake.loadPackageMetadataMutex.Unlock() + fake.LoadPackageMetadataStub = stub +} + +func (fake *FakeImpl) LoadPackageMetadataArgsForCall(i int) string { + fake.loadPackageMetadataMutex.RLock() + defer fake.loadPackageMetadataMutex.RUnlock() + argsForCall := fake.loadPackageMetadataArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) LoadPackageMetadataReturns(result1 metadata.PackageMetadataList, result2 error) { + fake.loadPackageMetadataMutex.Lock() + defer fake.loadPackageMetadataMutex.Unlock() + fake.LoadPackageMetadataStub = nil + fake.loadPackageMetadataReturns = struct { + result1 metadata.PackageMetadataList + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) LoadPackageMetadataReturnsOnCall(i int, result1 metadata.PackageMetadataList, result2 error) { + fake.loadPackageMetadataMutex.Lock() + defer fake.loadPackageMetadataMutex.Unlock() + fake.LoadPackageMetadataStub = nil + if fake.loadPackageMetadataReturnsOnCall == nil { + fake.loadPackageMetadataReturnsOnCall = make(map[int]struct { + result1 metadata.PackageMetadataList + result2 error + }) + } + fake.loadPackageMetadataReturnsOnCall[i] = struct { + result1 metadata.PackageMetadataList + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) Mkdir(arg1 string, arg2 fs.FileMode) error { + fake.mkdirMutex.Lock() + ret, specificReturn := fake.mkdirReturnsOnCall[len(fake.mkdirArgsForCall)] + fake.mkdirArgsForCall = append(fake.mkdirArgsForCall, struct { + arg1 string + arg2 fs.FileMode + }{arg1, arg2}) + stub := fake.MkdirStub + fakeReturns := fake.mkdirReturns + fake.recordInvocation("Mkdir", []interface{}{arg1, arg2}) + fake.mkdirMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) MkdirCallCount() int { + fake.mkdirMutex.RLock() + defer fake.mkdirMutex.RUnlock() + return len(fake.mkdirArgsForCall) +} + +func (fake *FakeImpl) MkdirCalls(stub func(string, fs.FileMode) error) { + fake.mkdirMutex.Lock() + defer fake.mkdirMutex.Unlock() + fake.MkdirStub = stub +} + +func (fake *FakeImpl) MkdirArgsForCall(i int) (string, fs.FileMode) { + fake.mkdirMutex.RLock() + defer fake.mkdirMutex.RUnlock() + argsForCall := fake.mkdirArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeImpl) MkdirReturns(result1 error) { + fake.mkdirMutex.Lock() + defer fake.mkdirMutex.Unlock() + fake.MkdirStub = nil + fake.mkdirReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) MkdirReturnsOnCall(i int, result1 error) { + fake.mkdirMutex.Lock() + defer fake.mkdirMutex.Unlock() + fake.MkdirStub = nil + if fake.mkdirReturnsOnCall == nil { + fake.mkdirReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.mkdirReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) MkdirAll(arg1 string, arg2 fs.FileMode) error { + fake.mkdirAllMutex.Lock() + ret, specificReturn := fake.mkdirAllReturnsOnCall[len(fake.mkdirAllArgsForCall)] + fake.mkdirAllArgsForCall = append(fake.mkdirAllArgsForCall, struct { + arg1 string + arg2 fs.FileMode + }{arg1, arg2}) + stub := fake.MkdirAllStub + fakeReturns := fake.mkdirAllReturns + fake.recordInvocation("MkdirAll", []interface{}{arg1, arg2}) + fake.mkdirAllMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) MkdirAllCallCount() int { + fake.mkdirAllMutex.RLock() + defer fake.mkdirAllMutex.RUnlock() + return len(fake.mkdirAllArgsForCall) +} + +func (fake *FakeImpl) MkdirAllCalls(stub func(string, fs.FileMode) error) { + fake.mkdirAllMutex.Lock() + defer fake.mkdirAllMutex.Unlock() + fake.MkdirAllStub = stub +} + +func (fake *FakeImpl) MkdirAllArgsForCall(i int) (string, fs.FileMode) { + fake.mkdirAllMutex.RLock() + defer fake.mkdirAllMutex.RUnlock() + argsForCall := fake.mkdirAllArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeImpl) MkdirAllReturns(result1 error) { + fake.mkdirAllMutex.Lock() + defer fake.mkdirAllMutex.Unlock() + fake.MkdirAllStub = nil + fake.mkdirAllReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) MkdirAllReturnsOnCall(i int, result1 error) { + fake.mkdirAllMutex.Lock() + defer fake.mkdirAllMutex.Unlock() + fake.MkdirAllStub = nil + if fake.mkdirAllReturnsOnCall == nil { + fake.mkdirAllReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.mkdirAllReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) RemoveAll(arg1 string) error { + fake.removeAllMutex.Lock() + ret, specificReturn := fake.removeAllReturnsOnCall[len(fake.removeAllArgsForCall)] + fake.removeAllArgsForCall = append(fake.removeAllArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.RemoveAllStub + fakeReturns := fake.removeAllReturns + fake.recordInvocation("RemoveAll", []interface{}{arg1}) + fake.removeAllMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) RemoveAllCallCount() int { + fake.removeAllMutex.RLock() + defer fake.removeAllMutex.RUnlock() + return len(fake.removeAllArgsForCall) +} + +func (fake *FakeImpl) RemoveAllCalls(stub func(string) error) { + fake.removeAllMutex.Lock() + defer fake.removeAllMutex.Unlock() + fake.RemoveAllStub = stub +} + +func (fake *FakeImpl) RemoveAllArgsForCall(i int) string { + fake.removeAllMutex.RLock() + defer fake.removeAllMutex.RUnlock() + argsForCall := fake.removeAllArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) RemoveAllReturns(result1 error) { + fake.removeAllMutex.Lock() + defer fake.removeAllMutex.Unlock() + fake.RemoveAllStub = nil + fake.removeAllReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) RemoveAllReturnsOnCall(i int, result1 error) { + fake.removeAllMutex.Lock() + defer fake.removeAllMutex.Unlock() + fake.RemoveAllStub = nil + if fake.removeAllReturnsOnCall == nil { + fake.removeAllReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.removeAllReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) RemoveFile(arg1 string) error { + fake.removeFileMutex.Lock() + ret, specificReturn := fake.removeFileReturnsOnCall[len(fake.removeFileArgsForCall)] + fake.removeFileArgsForCall = append(fake.removeFileArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.RemoveFileStub + fakeReturns := fake.removeFileReturns + fake.recordInvocation("RemoveFile", []interface{}{arg1}) + fake.removeFileMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) RemoveFileCallCount() int { + fake.removeFileMutex.RLock() + defer fake.removeFileMutex.RUnlock() + return len(fake.removeFileArgsForCall) +} + +func (fake *FakeImpl) RemoveFileCalls(stub func(string) error) { + fake.removeFileMutex.Lock() + defer fake.removeFileMutex.Unlock() + fake.RemoveFileStub = stub +} + +func (fake *FakeImpl) RemoveFileArgsForCall(i int) string { + fake.removeFileMutex.RLock() + defer fake.removeFileMutex.RUnlock() + argsForCall := fake.removeFileArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) RemoveFileReturns(result1 error) { + fake.removeFileMutex.Lock() + defer fake.removeFileMutex.Unlock() + fake.RemoveFileStub = nil + fake.removeFileReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) RemoveFileReturnsOnCall(i int, result1 error) { + fake.removeFileMutex.Lock() + defer fake.removeFileMutex.Unlock() + fake.RemoveFileStub = nil + if fake.removeFileReturnsOnCall == nil { + fake.removeFileReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.removeFileReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) Stat(arg1 string) (fs.FileInfo, error) { + fake.statMutex.Lock() + ret, specificReturn := fake.statReturnsOnCall[len(fake.statArgsForCall)] + fake.statArgsForCall = append(fake.statArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.StatStub + fakeReturns := fake.statReturns + fake.recordInvocation("Stat", []interface{}{arg1}) + fake.statMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeImpl) StatCallCount() int { + fake.statMutex.RLock() + defer fake.statMutex.RUnlock() + return len(fake.statArgsForCall) +} + +func (fake *FakeImpl) StatCalls(stub func(string) (fs.FileInfo, error)) { + fake.statMutex.Lock() + defer fake.statMutex.Unlock() + fake.StatStub = stub +} + +func (fake *FakeImpl) StatArgsForCall(i int) string { + fake.statMutex.RLock() + defer fake.statMutex.RUnlock() + argsForCall := fake.statArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) StatReturns(result1 fs.FileInfo, result2 error) { + fake.statMutex.Lock() + defer fake.statMutex.Unlock() + fake.StatStub = nil + fake.statReturns = struct { + result1 fs.FileInfo + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) StatReturnsOnCall(i int, result1 fs.FileInfo, result2 error) { + fake.statMutex.Lock() + defer fake.statMutex.Unlock() + fake.StatStub = nil + if fake.statReturnsOnCall == nil { + fake.statReturnsOnCall = make(map[int]struct { + result1 fs.FileInfo + result2 error + }) + } + fake.statReturnsOnCall[i] = struct { + result1 fs.FileInfo + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) TagStringToSemver(arg1 string) (semver.Version, error) { + fake.tagStringToSemverMutex.Lock() + ret, specificReturn := fake.tagStringToSemverReturnsOnCall[len(fake.tagStringToSemverArgsForCall)] + fake.tagStringToSemverArgsForCall = append(fake.tagStringToSemverArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.TagStringToSemverStub + fakeReturns := fake.tagStringToSemverReturns + fake.recordInvocation("TagStringToSemver", []interface{}{arg1}) + fake.tagStringToSemverMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeImpl) TagStringToSemverCallCount() int { + fake.tagStringToSemverMutex.RLock() + defer fake.tagStringToSemverMutex.RUnlock() + return len(fake.tagStringToSemverArgsForCall) +} + +func (fake *FakeImpl) TagStringToSemverCalls(stub func(string) (semver.Version, error)) { + fake.tagStringToSemverMutex.Lock() + defer fake.tagStringToSemverMutex.Unlock() + fake.TagStringToSemverStub = stub +} + +func (fake *FakeImpl) TagStringToSemverArgsForCall(i int) string { + fake.tagStringToSemverMutex.RLock() + defer fake.tagStringToSemverMutex.RUnlock() + argsForCall := fake.tagStringToSemverArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) TagStringToSemverReturns(result1 semver.Version, result2 error) { + fake.tagStringToSemverMutex.Lock() + defer fake.tagStringToSemverMutex.Unlock() + fake.TagStringToSemverStub = nil + fake.tagStringToSemverReturns = struct { + result1 semver.Version + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) TagStringToSemverReturnsOnCall(i int, result1 semver.Version, result2 error) { + fake.tagStringToSemverMutex.Lock() + defer fake.tagStringToSemverMutex.Unlock() + fake.TagStringToSemverStub = nil + if fake.tagStringToSemverReturnsOnCall == nil { + fake.tagStringToSemverReturnsOnCall = make(map[int]struct { + result1 semver.Version + result2 error + }) + } + fake.tagStringToSemverReturnsOnCall[i] = struct { + result1 semver.Version + result2 error + }{result1, result2} +} + +func (fake *FakeImpl) TrimTagPrefix(arg1 string) string { + fake.trimTagPrefixMutex.Lock() + ret, specificReturn := fake.trimTagPrefixReturnsOnCall[len(fake.trimTagPrefixArgsForCall)] + fake.trimTagPrefixArgsForCall = append(fake.trimTagPrefixArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.TrimTagPrefixStub + fakeReturns := fake.trimTagPrefixReturns + fake.recordInvocation("TrimTagPrefix", []interface{}{arg1}) + fake.trimTagPrefixMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) TrimTagPrefixCallCount() int { + fake.trimTagPrefixMutex.RLock() + defer fake.trimTagPrefixMutex.RUnlock() + return len(fake.trimTagPrefixArgsForCall) +} + +func (fake *FakeImpl) TrimTagPrefixCalls(stub func(string) string) { + fake.trimTagPrefixMutex.Lock() + defer fake.trimTagPrefixMutex.Unlock() + fake.TrimTagPrefixStub = stub +} + +func (fake *FakeImpl) TrimTagPrefixArgsForCall(i int) string { + fake.trimTagPrefixMutex.RLock() + defer fake.trimTagPrefixMutex.RUnlock() + argsForCall := fake.trimTagPrefixArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) TrimTagPrefixReturns(result1 string) { + fake.trimTagPrefixMutex.Lock() + defer fake.trimTagPrefixMutex.Unlock() + fake.TrimTagPrefixStub = nil + fake.trimTagPrefixReturns = struct { + result1 string + }{result1} +} + +func (fake *FakeImpl) TrimTagPrefixReturnsOnCall(i int, result1 string) { + fake.trimTagPrefixMutex.Lock() + defer fake.trimTagPrefixMutex.Unlock() + fake.TrimTagPrefixStub = nil + if fake.trimTagPrefixReturnsOnCall == nil { + fake.trimTagPrefixReturnsOnCall = make(map[int]struct { + result1 string + }) + } + fake.trimTagPrefixReturnsOnCall[i] = struct { + result1 string + }{result1} +} + +func (fake *FakeImpl) Walk(arg1 string, arg2 filepath.WalkFunc) error { + fake.walkMutex.Lock() + ret, specificReturn := fake.walkReturnsOnCall[len(fake.walkArgsForCall)] + fake.walkArgsForCall = append(fake.walkArgsForCall, struct { + arg1 string + arg2 filepath.WalkFunc + }{arg1, arg2}) + stub := fake.WalkStub + fakeReturns := fake.walkReturns + fake.recordInvocation("Walk", []interface{}{arg1, arg2}) + fake.walkMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) WalkCallCount() int { + fake.walkMutex.RLock() + defer fake.walkMutex.RUnlock() + return len(fake.walkArgsForCall) +} + +func (fake *FakeImpl) WalkCalls(stub func(string, filepath.WalkFunc) error) { + fake.walkMutex.Lock() + defer fake.walkMutex.Unlock() + fake.WalkStub = stub +} + +func (fake *FakeImpl) WalkArgsForCall(i int) (string, filepath.WalkFunc) { + fake.walkMutex.RLock() + defer fake.walkMutex.RUnlock() + argsForCall := fake.walkArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeImpl) WalkReturns(result1 error) { + fake.walkMutex.Lock() + defer fake.walkMutex.Unlock() + fake.WalkStub = nil + fake.walkReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) WalkReturnsOnCall(i int, result1 error) { + fake.walkMutex.Lock() + defer fake.walkMutex.Unlock() + fake.WalkStub = nil + if fake.walkReturnsOnCall == nil { + fake.walkReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.walkReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) WriteFile(arg1 string, arg2 []byte, arg3 fs.FileMode) error { + var arg2Copy []byte + if arg2 != nil { + arg2Copy = make([]byte, len(arg2)) + copy(arg2Copy, arg2) + } + fake.writeFileMutex.Lock() + ret, specificReturn := fake.writeFileReturnsOnCall[len(fake.writeFileArgsForCall)] + fake.writeFileArgsForCall = append(fake.writeFileArgsForCall, struct { + arg1 string + arg2 []byte + arg3 fs.FileMode + }{arg1, arg2Copy, arg3}) + stub := fake.WriteFileStub + fakeReturns := fake.writeFileReturns + fake.recordInvocation("WriteFile", []interface{}{arg1, arg2Copy, arg3}) + fake.writeFileMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) WriteFileCallCount() int { + fake.writeFileMutex.RLock() + defer fake.writeFileMutex.RUnlock() + return len(fake.writeFileArgsForCall) +} + +func (fake *FakeImpl) WriteFileCalls(stub func(string, []byte, fs.FileMode) error) { + fake.writeFileMutex.Lock() + defer fake.writeFileMutex.Unlock() + fake.WriteFileStub = stub +} + +func (fake *FakeImpl) WriteFileArgsForCall(i int) (string, []byte, fs.FileMode) { + fake.writeFileMutex.RLock() + defer fake.writeFileMutex.RUnlock() + argsForCall := fake.writeFileArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeImpl) WriteFileReturns(result1 error) { + fake.writeFileMutex.Lock() + defer fake.writeFileMutex.Unlock() + fake.WriteFileStub = nil + fake.writeFileReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) WriteFileReturnsOnCall(i int, result1 error) { + fake.writeFileMutex.Lock() + defer fake.writeFileMutex.Unlock() + fake.WriteFileStub = nil + if fake.writeFileReturnsOnCall == nil { + fake.writeFileReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.writeFileReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeImpl) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() + fake.compressMutex.RLock() + defer fake.compressMutex.RUnlock() + fake.createFileMutex.RLock() + defer fake.createFileMutex.RUnlock() + fake.extractMutex.RLock() + defer fake.extractMutex.RUnlock() + fake.gCSCopyToLocalMutex.RLock() + defer fake.gCSCopyToLocalMutex.RUnlock() fake.getKubeVersionMutex.RLock() defer fake.getKubeVersionMutex.RUnlock() fake.getRequestMutex.RLock() defer fake.getRequestMutex.RUnlock() + fake.isExistMutex.RLock() + defer fake.isExistMutex.RUnlock() + fake.loadPackageMetadataMutex.RLock() + defer fake.loadPackageMetadataMutex.RUnlock() + fake.mkdirMutex.RLock() + defer fake.mkdirMutex.RUnlock() + fake.mkdirAllMutex.RLock() + defer fake.mkdirAllMutex.RUnlock() + fake.removeAllMutex.RLock() + defer fake.removeAllMutex.RUnlock() + fake.removeFileMutex.RLock() + defer fake.removeFileMutex.RUnlock() + fake.statMutex.RLock() + defer fake.statMutex.RUnlock() + fake.tagStringToSemverMutex.RLock() + defer fake.tagStringToSemverMutex.RUnlock() + fake.trimTagPrefixMutex.RLock() + defer fake.trimTagPrefixMutex.RUnlock() + fake.walkMutex.RLock() + defer fake.walkMutex.RUnlock() + fake.writeFileMutex.RLock() + defer fake.writeFileMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value @@ -193,5 +1367,3 @@ func (fake *FakeImpl) recordInvocation(key string, args []interface{}) { } fake.invocations[key] = append(fake.invocations[key], args) } - -var _ specs.Impl = new(FakeImpl) diff --git a/pkg/obs/specs/template.go b/pkg/obs/specs/template.go index 230d7977372..07726afdeaa 100644 --- a/pkg/obs/specs/template.go +++ b/pkg/obs/specs/template.go @@ -36,7 +36,7 @@ type work struct { } // BuildSpecs creates spec file based on provided package definition. -func (c *Client) BuildSpecs(pkgDef *PackageDefinition, specOnly bool) (err error) { +func (s *Specs) BuildSpecs(pkgDef *PackageDefinition, specOnly bool) (err error) { if pkgDef == nil { return errors.New("package definition cannot be nil") } @@ -44,11 +44,11 @@ func (c *Client) BuildSpecs(pkgDef *PackageDefinition, specOnly bool) (err error workItems := []work{} tplDir := filepath.Join(pkgDef.SpecTemplatePath, pkgDef.Name) - if _, err := os.Stat(tplDir); err != nil { + if _, err := s.impl.Stat(tplDir); err != nil { return fmt.Errorf("building specs for %s: finding package template dir: %w", pkgDef.Name, err) } - if err := filepath.Walk(tplDir, func(templateFile string, f os.FileInfo, err error) error { + if err := s.impl.Walk(tplDir, func(templateFile string, f os.FileInfo, err error) error { if err != nil { return err } @@ -60,7 +60,7 @@ func (c *Client) BuildSpecs(pkgDef *PackageDefinition, specOnly bool) (err error } if f.IsDir() { - return os.Mkdir(specFile, f.Mode()) + return s.impl.Mkdir(specFile, f.Mode()) } if filepath.Ext(templateFile) == ".spec" { // Spec is intentionally saved outside package dir, which is later on archived @@ -96,7 +96,7 @@ func (c *Client) BuildSpecs(pkgDef *PackageDefinition, specOnly bool) (err error return fmt.Errorf("executing template for %s: %w", item.src, err) } - if err := os.WriteFile( + if err := s.impl.WriteFile( item.dst, buf.Bytes(), item.info.Mode(), ); err != nil { return fmt.Errorf("writing file %s: %w", item.dst, err)