From 48f5f90c9884edbecbae2fdcd57ae53b6f439d61 Mon Sep 17 00:00:00 2001 From: 24sama Date: Fri, 25 Feb 2022 15:14:49 +0800 Subject: [PATCH] add images push command Signed-off-by: 24sama --- cmd/ctl/artifact/artifact.go | 6 +- cmd/ctl/artifact/export.go | 8 +- cmd/ctl/artifact/images/images.go | 47 ++++++++++ cmd/ctl/artifact/images/push.go | 140 ++++++++++++++++++++++++++++++ pkg/bootstrap/precheck/module.go | 19 ++++ pkg/bootstrap/precheck/tasks.go | 12 +++ pkg/common/kube_runtime.go | 1 + pkg/filesystem/module.go | 20 ++++- pkg/images/module.go | 5 +- pkg/images/tasks.go | 9 +- pkg/pipelines/artifact_export.go | 2 + 11 files changed, 256 insertions(+), 13 deletions(-) create mode 100644 cmd/ctl/artifact/images/images.go create mode 100644 cmd/ctl/artifact/images/push.go diff --git a/cmd/ctl/artifact/artifact.go b/cmd/ctl/artifact/artifact.go index 8a2ce768..ad4458e2 100644 --- a/cmd/ctl/artifact/artifact.go +++ b/cmd/ctl/artifact/artifact.go @@ -17,6 +17,7 @@ package artifact import ( + "github.com/kubesphere/kubekey/cmd/ctl/artifact/images" "github.com/kubesphere/kubekey/cmd/ctl/options" "github.com/spf13/cobra" ) @@ -31,7 +32,7 @@ func NewArtifactOptions() *ArtifactOptions { } } -// NewCmdArtifact creates a new create command +// NewCmdArtifact creates a new cobra.Command for `kubekey artifact` func NewCmdArtifact() *cobra.Command { o := NewArtifactOptions() cmd := &cobra.Command{ @@ -41,6 +42,7 @@ func NewCmdArtifact() *cobra.Command { o.CommonOptions.AddCommonFlag(cmd) - cmd.AddCommand(NewCmdCreateCluster()) + cmd.AddCommand(NewCmdArtifactExport()) + cmd.AddCommand(images.NewCmdArtifactImages()) return cmd } diff --git a/cmd/ctl/artifact/export.go b/cmd/ctl/artifact/export.go index 3b3820c2..b6cd2fa2 100644 --- a/cmd/ctl/artifact/export.go +++ b/cmd/ctl/artifact/export.go @@ -42,8 +42,8 @@ func NewArtifactExportOptions() *ArtifactExportOptions { } } -// NewCmdCreateCluster creates a new create cluster command -func NewCmdCreateCluster() *cobra.Command { +// NewCmdArtifactExport creates a new `kubekey artifact export` command +func NewCmdArtifactExport() *cobra.Command { o := NewArtifactExportOptions() cmd := &cobra.Command{ Use: "export", @@ -60,7 +60,7 @@ func NewCmdCreateCluster() *cobra.Command { return cmd } -func (o *ArtifactExportOptions) Complete(cmd *cobra.Command, args []string) error { +func (o *ArtifactExportOptions) Complete(_ *cobra.Command, _ []string) error { var err error if o.Output == "" { o.Output = "kubekey-artifact.tar.gz" @@ -74,7 +74,7 @@ func (o *ArtifactExportOptions) Complete(cmd *cobra.Command, args []string) erro return nil } -func (o *ArtifactExportOptions) Validate(args []string) error { +func (o *ArtifactExportOptions) Validate(_ []string) error { if o.ManifestFile == "" { return fmt.Errorf("--manifest can not be an empty string") } diff --git a/cmd/ctl/artifact/images/images.go b/cmd/ctl/artifact/images/images.go new file mode 100644 index 00000000..2cc45c3d --- /dev/null +++ b/cmd/ctl/artifact/images/images.go @@ -0,0 +1,47 @@ +/* + Copyright 2022 The KubeSphere 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 images + +import ( + "github.com/kubesphere/kubekey/cmd/ctl/options" + "github.com/spf13/cobra" +) + +type ArtifactImagesOptions struct { + CommonOptions *options.CommonOptions +} + +func NewArtifactImagesOptions() *ArtifactImagesOptions { + return &ArtifactImagesOptions{ + CommonOptions: options.NewCommonOptions(), + } +} + +// NewCmdArtifactImages creates a new `kubekey artifact image` command +func NewCmdArtifactImages() *cobra.Command { + o := NewArtifactImagesOptions() + cmd := &cobra.Command{ + Use: "images", + Aliases: []string{"image", "i"}, + Short: "manage KubeKey artifact image", + } + + o.CommonOptions.AddCommonFlag(cmd) + cmd.AddCommand(NewCmdArtifactImagesPush()) + + return cmd +} diff --git a/cmd/ctl/artifact/images/push.go b/cmd/ctl/artifact/images/push.go new file mode 100644 index 00000000..0775415f --- /dev/null +++ b/cmd/ctl/artifact/images/push.go @@ -0,0 +1,140 @@ +/* + Copyright 2022 The KubeSphere 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 images + +import ( + "github.com/kubesphere/kubekey/cmd/ctl/options" + "github.com/kubesphere/kubekey/cmd/ctl/util" + "github.com/kubesphere/kubekey/pkg/artifact" + "github.com/kubesphere/kubekey/pkg/bootstrap/precheck" + "github.com/kubesphere/kubekey/pkg/common" + "github.com/kubesphere/kubekey/pkg/core/module" + "github.com/kubesphere/kubekey/pkg/core/pipeline" + "github.com/kubesphere/kubekey/pkg/filesystem" + "github.com/kubesphere/kubekey/pkg/images" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "os" + "path/filepath" +) + +type ArtifactImagesPushOptions struct { + CommonOptions *options.CommonOptions + + ImageDirPath string + Artifact string + ClusterCfgFile string +} + +func NewArtifactImagesPushOptions() *ArtifactImagesPushOptions { + return &ArtifactImagesPushOptions{ + CommonOptions: options.NewCommonOptions(), + } +} + +// NewCmdArtifactImagesPush creates a new `kubekey artifacts images push` command +func NewCmdArtifactImagesPush() *cobra.Command { + o := NewArtifactImagesPushOptions() + cmd := &cobra.Command{ + Use: "push", + Short: "push images to a registry from an artifact", + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(o.Complete(cmd, args)) + util.CheckErr(o.Validate(args)) + util.CheckErr(o.Run()) + }, + } + + o.CommonOptions.AddCommonFlag(cmd) + o.AddFlags(cmd) + return cmd +} + +func (o *ArtifactImagesPushOptions) Complete(_ *cobra.Command, _ []string) error { + if o.ImageDirPath == "" && o.Artifact == "" { + currentDir, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + return errors.Wrap(err, "failed to get current directory") + } + o.ImageDirPath = filepath.Join(currentDir, "kubekey", "images") + } + + return nil +} + +func (o *ArtifactImagesPushOptions) Validate(_ []string) error { + if o.ClusterCfgFile == "" { + return errors.New("kubekey config file is required") + } + if o.ImageDirPath != "" && o.Artifact != "" { + return errors.New("only one of --image-dir or --artifact can be specified") + } + return nil +} + +func (o *ArtifactImagesPushOptions) Run() error { + arg := common.Argument{ + ImagesDir: o.ImageDirPath, + Artifact: o.Artifact, + FilePath: o.ClusterCfgFile, + Debug: o.CommonOptions.Verbose, + IgnoreErr: o.CommonOptions.IgnoreErr, + } + return runPush(arg) +} + +func (o *ArtifactImagesPushOptions) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVarP(&o.ImageDirPath, "images-dir", "", "", "Path to a KubeKey artifact images directory") + cmd.Flags().StringVarP(&o.Artifact, "artifact", "a", "", "Path to a KubeKey artifact") + cmd.Flags().StringVarP(&o.ClusterCfgFile, "filename", "f", "", "Path to a configuration file") +} + +func runPush(arg common.Argument) error { + runtime, err := common.NewKubeRuntime(common.File, arg) + if err != nil { + return err + } + + if err := newImagesPushPipeline(runtime); err != nil { + return err + } + + return nil +} + +func newImagesPushPipeline(runtime *common.KubeRuntime) error { + noArtifact := runtime.Arg.Artifact == "" + + m := []module.Module{ + &precheck.CRIPreCheckModule{}, + &artifact.UnArchiveModule{Skip: noArtifact}, + &images.PushModule{ImagePath: runtime.Arg.ImagesDir}, + &filesystem.ChownWorkDirModule{}, + } + + p := pipeline.Pipeline{ + Name: "ArtifactImagesPushPipeline", + Modules: m, + Runtime: runtime, + } + + if err := p.Start(); err != nil { + return err + } + + return nil +} diff --git a/pkg/bootstrap/precheck/module.go b/pkg/bootstrap/precheck/module.go index 78276dc0..2f0dd85e 100644 --- a/pkg/bootstrap/precheck/module.go +++ b/pkg/bootstrap/precheck/module.go @@ -139,3 +139,22 @@ func (c *ClusterPreCheckModule) Init() { getKubernetesNodesStatus, } } + +type CRIPreCheckModule struct { + common.KubeModule +} + +func (c *CRIPreCheckModule) Init() { + c.Name = "CRIPreCheckModule" + c.Desc = "Do CRI pre-check on local node" + + criCheck := &task.LocalTask{ + Name: "CRIPreCheck", + Desc: "Check CRI", + Action: new(CRIPreCheck), + } + + c.Tasks = []task.Interface{ + criCheck, + } +} diff --git a/pkg/bootstrap/precheck/tasks.go b/pkg/bootstrap/precheck/tasks.go index 235d2e81..745d3647 100644 --- a/pkg/bootstrap/precheck/tasks.go +++ b/pkg/bootstrap/precheck/tasks.go @@ -24,6 +24,7 @@ import ( "github.com/kubesphere/kubekey/pkg/version/kubesphere" "github.com/pkg/errors" versionutil "k8s.io/apimachinery/pkg/util/version" + "os/exec" "regexp" "strings" ) @@ -275,3 +276,14 @@ func (g *GetKubernetesNodesStatus) Execute(runtime connector.Runtime) error { g.PipelineCache.Set(common.ClusterNodeStatus, nodeStatus) return nil } + +type CRIPreCheck struct { + common.KubeAction +} + +func (c *CRIPreCheck) Execute(_ connector.Runtime) error { + if _, err := exec.Command("/bin/bash", "-c", "which ctr").CombinedOutput(); err != nil { + return errors.New("containerd is not installed") + } + return nil +} diff --git a/pkg/common/kube_runtime.go b/pkg/common/kube_runtime.go index dde9192a..827a0101 100644 --- a/pkg/common/kube_runtime.go +++ b/pkg/common/kube_runtime.go @@ -53,6 +53,7 @@ type Argument struct { Artifact string InstallPackages bool CertificatesDir string + ImagesDir string } func NewKubeRuntime(flag string, arg Argument) (*KubeRuntime, error) { diff --git a/pkg/filesystem/module.go b/pkg/filesystem/module.go index 20db2ba9..af2f1d40 100644 --- a/pkg/filesystem/module.go +++ b/pkg/filesystem/module.go @@ -44,12 +44,12 @@ func (c *ChownModule) Init() { } type ChownWorkDirModule struct { - common.ArtifactModule + module.BaseTaskModule } func (c *ChownWorkDirModule) Init() { - c.Name = "ChownWorkDirModule" - c.Desc = "Change file and dir owner" + c.Name = "ChownWorkerModule" + c.Desc = "Change kubekey work dir mode and owner" userKubeDir := &task.LocalTask{ Name: "ChownFileAndDir", @@ -57,6 +57,19 @@ func (c *ChownWorkDirModule) Init() { Action: &LocalTaskChown{Path: c.Runtime.GetWorkDir()}, } + c.Tasks = []task.Interface{ + userKubeDir, + } +} + +type ChownOutputModule struct { + common.ArtifactModule +} + +func (c *ChownOutputModule) Init() { + c.Name = "ChownOutputModule" + c.Desc = "Change file and dir owner" + output := &task.LocalTask{ Name: "Chown output file", Desc: "Chown output file", @@ -64,7 +77,6 @@ func (c *ChownWorkDirModule) Init() { } c.Tasks = []task.Interface{ - userKubeDir, output, } } diff --git a/pkg/images/module.go b/pkg/images/module.go index 0ff4ac38..604236b0 100644 --- a/pkg/images/module.go +++ b/pkg/images/module.go @@ -73,7 +73,8 @@ func (p *PullModule) Init() { type PushModule struct { common.KubeModule - Skip bool + Skip bool + ImagePath string } func (p *PushModule) IsSkip() bool { @@ -87,7 +88,7 @@ func (p *PushModule) Init() { push := &task.LocalTask{ Name: "PushImages", Desc: "Push images to private registry", - Action: new(PushImage), + Action: &PushImage{ImagesPath: p.ImagePath}, } p.Tasks = []task.Interface{ diff --git a/pkg/images/tasks.go b/pkg/images/tasks.go index 14dd6744..c02886d6 100644 --- a/pkg/images/tasks.go +++ b/pkg/images/tasks.go @@ -136,10 +136,17 @@ func GetImage(runtime connector.ModuleRuntime, kubeConf *common.KubeConf, name s type PushImage struct { common.KubeAction + ImagesPath string } func (p *PushImage) Execute(runtime connector.Runtime) error { - imagesPath := filepath.Join(runtime.GetWorkDir(), "images") + var imagesPath string + if p.ImagesPath != "" { + imagesPath = p.ImagesPath + } else { + imagesPath = filepath.Join(runtime.GetWorkDir(), "images") + } + files, err := ioutil.ReadDir(imagesPath) if err != nil { return errors.Wrapf(errors.WithStack(err), "read %s dir faied", imagesPath) diff --git a/pkg/pipelines/artifact_export.go b/pkg/pipelines/artifact_export.go index 08b0ba47..11e485d4 100644 --- a/pkg/pipelines/artifact_export.go +++ b/pkg/pipelines/artifact_export.go @@ -35,6 +35,7 @@ func NewArtifactExportPipeline(runtime *common.ArtifactRuntime) error { &binaries.ArtifactBinariesModule{}, &artifact.RepositoryModule{}, &artifact.ArchiveModule{}, + &filesystem.ChownOutputModule{}, &filesystem.ChownWorkDirModule{}, } @@ -58,6 +59,7 @@ func NewK3sArtifactExportPipeline(runtime *common.ArtifactRuntime) error { &binaries.K3sArtifactBinariesModule{}, &artifact.RepositoryModule{}, &artifact.ArchiveModule{}, + &filesystem.ChownOutputModule{}, &filesystem.ChownWorkDirModule{}, }