diff --git a/cmd/sealer/cmd/alpha/alpha.go b/cmd/sealer/cmd/alpha/alpha.go index f1e864342f2..8fa3fbfdee0 100644 --- a/cmd/sealer/cmd/alpha/alpha.go +++ b/cmd/sealer/cmd/alpha/alpha.go @@ -39,5 +39,7 @@ func NewCmdAlpha() *cobra.Command { cmd.AddCommand(NewSearchCmd()) cmd.AddCommand(NewManifestCmd()) cmd.AddCommand(NewHostAliasCmd()) + cmd.AddCommand(NewMountCmd()) + cmd.AddCommand(NewUmountCmd()) return cmd } diff --git a/cmd/sealer/cmd/alpha/mount.go b/cmd/sealer/cmd/alpha/mount.go new file mode 100644 index 00000000000..cbe541866fd --- /dev/null +++ b/cmd/sealer/cmd/alpha/mount.go @@ -0,0 +1,119 @@ +// Copyright © 2021 Alibaba Group Holding Ltd. +// +// 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 alpha + +import ( + "os" + "path/filepath" + "strings" + + "github.com/containers/storage" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/olekukonko/tablewriter" + "github.com/sealerio/sealer/common" + "github.com/sealerio/sealer/pkg/define/options" + "github.com/sealerio/sealer/pkg/imageengine" + "github.com/sealerio/sealer/pkg/imageengine/buildah" +) + +var ( + path string + imageID string +) + +var longMountCmdDescription = ` +mount the cluster image to '/var/lib/sealer/data/overlay2' the directory and check whether the contents of the build image and rootfs are consistent in advance +` + +var exampleForMountCmd = ` + sealer alpha mount(show mount list) + sealer alpha mount my-image + sealer alpha mount ba15e47f5969 +` + +func NewMountCmd() *cobra.Command { + mountCmd := &cobra.Command{ + Use: "mount", + Short: "mount cluster image", + Long: longMountCmdDescription, + Example: exampleForMountCmd, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + engine, err := buildah.NewBuildahImageEngine(options.EngineGlobalConfigurations{}) + if err != nil { + return err + } + + store := engine.ImageStore() + images, err := store.Images() + if err != nil { + return err + } + + //output mount list + if len(args) == 0 { + if err := mountList(images); err != nil { + return err + } + return nil + } + + for _, i := range images { + for _, name := range i.Names { + if name == args[0] || strings.Contains(i.ID, args[0]) { + imageID = i.ID + path = filepath.Join(common.DefaultLayerDir, imageID) + } + } + } + + imageEngine, err := imageengine.NewImageEngine(options.EngineGlobalConfigurations{}) + if err != nil { + return err + } + if _, err := imageEngine.CreateWorkingContainer(&options.BuildRootfsOptions{ + DestDir: path, + ImageNameOrID: args[0], + }); err != nil { + return err + } + logrus.Infof("mount cluster image %s to %s successful", args[0], path) + return nil + }, + } + return mountCmd +} + +func mountList(images []storage.Image) error { + table := tablewriter.NewWriter(common.StdOut) + table.SetHeader([]string{imageName, "mountpath"}) + for _, i := range images { + for _, name := range i.Names { + err := filepath.Walk(common.DefaultLayerDir, func(path string, f os.FileInfo, err error) error { + if f.Name() == i.ID { + table.Append([]string{name, filepath.Join(common.DefaultLayerDir, i.ID)}) + } + return nil + }) + if err != nil { + return err + } + } + } + table.Render() + return nil +} diff --git a/cmd/sealer/cmd/alpha/umount.go b/cmd/sealer/cmd/alpha/umount.go new file mode 100644 index 00000000000..4b675619f0c --- /dev/null +++ b/cmd/sealer/cmd/alpha/umount.go @@ -0,0 +1,168 @@ +// Copyright © 2021 Alibaba Group Holding Ltd. +// +// 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 alpha + +import ( + "fmt" + "io/fs" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/sealerio/sealer/common" + imagecommon "github.com/sealerio/sealer/pkg/define/options" + "github.com/sealerio/sealer/pkg/imageengine" + "github.com/sealerio/sealer/pkg/imageengine/buildah" + + "github.com/containers/storage" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var umountAll bool + +var longUmountCmdDescription = ` +umount the cluster image and delete the mount directory +` + +var exampleForUmountCmd = ` + sealer alpha umount my-image + sealer alpha umount ba15e47f5969 +` + +func NewUmountCmd() *cobra.Command { + umountCmd := &cobra.Command{ + Use: "umount", + Short: "umount cluster image", + Long: longUmountCmdDescription, + Example: exampleForUmountCmd, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + var cid string + var imgName string + + imageEngine, err := imageengine.NewImageEngine(imagecommon.EngineGlobalConfigurations{}) + if err != nil { + return err + } + + engine, err := buildah.NewBuildahImageEngine(imagecommon.EngineGlobalConfigurations{}) + if err != nil { + return err + } + + store := engine.ImageStore() + containers, err := store.Containers() + if err != nil { + return err + } + + files, err := ioutil.ReadDir(common.DefaultLayerDir) + if err != nil { + return err + } + + // umount all cluster image + if umountAll { + for _, c := range containers { + if err := imageEngine.RemoveContainer(&imagecommon.RemoveContainerOptions{ + ContainerNamesOrIDs: []string{c.ID}}, + ); err != nil { + return err + } + } + + for _, file := range files { + if err := os.RemoveAll(filepath.Join(common.DefaultLayerDir, file.Name())); err != nil { + return err + } + } + logrus.Infof("umount all cluster image successful") + return nil + } + + images, err := store.Images() + if err != nil { + return err + } + + for _, image := range images { + for _, name := range image.Names { + if strings.Contains(image.ID, args[0]) { + imgName = name + } + } + } + + for _, c := range containers { + if strings.Contains(c.ImageID, args[0]) { + cid = c.ID + break + } + + id, err := getImageID(images, args[0]) + if err != nil { + return err + } + if c.ImageID == id { + cid = c.ID + imgName = args[0] + } + } + + if err := imageEngine.RemoveContainer(&imagecommon.RemoveContainerOptions{ + ContainerNamesOrIDs: []string{cid}}, + ); err != nil { + return err + } + + for _, file := range files { + for _, image := range images { + if err := removeContainerDir(image, file, imgName); err != nil { + return err + } + } + } + + logrus.Infof("umount cluster image %s successful", args[0]) + return nil + }, + } + umountCmd.Flags().BoolVarP(&umountAll, "all", "a", false, "umount all cluster image directories") + return umountCmd +} + +func getImageID(images []storage.Image, imageName string) (string, error) { + for _, image := range images { + for _, n := range image.Names { + if n == imageName { + return image.ID, nil + } + } + } + return "", fmt.Errorf("failed to get container id") +} + +func removeContainerDir(image storage.Image, file fs.FileInfo, imageName string) error { + for _, n := range image.Names { + if n == imageName && file.Name() == image.ID { + if err := os.RemoveAll(filepath.Join(common.DefaultLayerDir, file.Name())); err != nil { + return err + } + } + } + return nil +} diff --git a/cmd/sealer/cmd/utils/cluster.go b/cmd/sealer/cmd/utils/cluster.go index c023dc92fee..821a7a325c0 100644 --- a/cmd/sealer/cmd/utils/cluster.go +++ b/cmd/sealer/cmd/utils/cluster.go @@ -26,13 +26,11 @@ import ( "github.com/sealerio/sealer/pkg/client/k8s" "github.com/sealerio/sealer/cmd/sealer/cmd/types" - - netutils "github.com/sealerio/sealer/utils/net" - strUtils "github.com/sealerio/sealer/utils/strings" - "github.com/sealerio/sealer/common" v1 "github.com/sealerio/sealer/types/api/v1" v2 "github.com/sealerio/sealer/types/api/v2" + netutils "github.com/sealerio/sealer/utils/net" + strUtils "github.com/sealerio/sealer/utils/strings" ) func MergeClusterWithFlags(cluster v2.Cluster, mergeFlags *types.MergeFlags) (*v2.Cluster, error) { diff --git a/cmd/sealer/cmd/utils/hosts.go b/cmd/sealer/cmd/utils/hosts.go index d452bbab715..fd8a6193546 100644 --- a/cmd/sealer/cmd/utils/hosts.go +++ b/cmd/sealer/cmd/utils/hosts.go @@ -20,7 +20,6 @@ import ( v1 "github.com/sealerio/sealer/types/api/v1" "github.com/sealerio/sealer/common" - v2 "github.com/sealerio/sealer/types/api/v2" ) diff --git a/pkg/imageengine/buildah/from.go b/pkg/imageengine/buildah/from.go index 541868965a2..52a360bb065 100644 --- a/pkg/imageengine/buildah/from.go +++ b/pkg/imageengine/buildah/from.go @@ -20,15 +20,16 @@ import ( "os" "strings" - "github.com/sealerio/sealer/pkg/define/options" - "github.com/containers/buildah" "github.com/containers/buildah/define" buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/buildah/pkg/parse" "github.com/containers/common/pkg/config" encconfig "github.com/containers/ocicrypt/config" + "github.com/pkg/errors" + + "github.com/sealerio/sealer/pkg/define/options" ) type fromFlagsWrapper struct {