diff --git a/pkg/image/binary/image.go b/pkg/image/binary/image.go index 36e6657ff..974c608b4 100644 --- a/pkg/image/binary/image.go +++ b/pkg/image/binary/image.go @@ -18,6 +18,10 @@ package binary import ( "fmt" + "path/filepath" + "strings" + + fileutil "github.com/fanux/sealos/pkg/utils/file" "github.com/fanux/sealos/pkg/image/types" @@ -33,12 +37,32 @@ import ( type ImageService struct { } -func (d *ImageService) Rename(src, dst string) error { - panic("implement me") +func (d *ImageService) Tag(src, dst string) error { + return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah tag %s %s", src, dst)) } -func (d *ImageService) Remove(images ...string) error { - panic("implement me") +func (d *ImageService) Save(imageName, archiveName string) error { + localDir := filepath.Dir(archiveName) + if !fileutil.IsExist(localDir) { + return errors.New("archive dir is not exist") + } + return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah push %s oci-archive:%s", imageName, archiveName)) +} + +func (d *ImageService) Load(archiveName string) error { + if !fileutil.IsExist(archiveName) { + return errors.New("archive file is not exist") + } + return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah pull oci-archive:%s", archiveName)) +} + +func (d *ImageService) Remove(force bool, images ...string) error { + var forceCMD string + if force { + forceCMD = "-f" + } + cmd := fmt.Sprintf("buildah rmi %s %s", forceCMD, strings.Join(images, " ")) + return exec.CmdForPipe("bash", "-c", cmd) } func (d *ImageService) Inspect(image string) (*v1.Image, error) { @@ -67,16 +91,35 @@ func inspectImage(data string) (*v1.Image, error) { return nil, errors.New("inspect output is empty") } -func (d *ImageService) Build(options types.BuildOptions, contextDir, imageName string) error { - panic("implement me") +func (d *ImageService) Build(options *types.BuildOptions, contextDir, imageName string) error { + options.Tag = imageName + cmd := fmt.Sprintf("buildah build %s %s", options.String(), contextDir) + return exec.CmdForPipe("bash", "-c", cmd) } func (d *ImageService) Prune() error { - panic("implement me") + return exec.CmdForPipe("bash", "-c", "buildah rmi --prune") } -func (d *ImageService) ListImages(opt types.ListOptions) ([]v1.Image, error) { - panic("implement me") +func (d *ImageService) ListImages() ([]types.ImageInfo, error) { + data := exec.BashEval("buildah images --json") + infos, err := listImage(data) + if err != nil { + return nil, err + } + return infos, nil +} + +func listImage(data string) ([]types.ImageInfo, error) { + if data != "" { + var outStruct []types.ImageInfo + err := json.Unmarshal([]byte(data), &outStruct) + if err != nil { + return nil, errors.Wrap(err, "decode out json from list images failed") + } + return outStruct, nil + } + return nil, errors.New("images output is empty") } func NewImageService() (types.Service, error) { diff --git a/pkg/image/binary/registry.go b/pkg/image/binary/registry.go index 22cb1a295..05a29b65e 100644 --- a/pkg/image/binary/registry.go +++ b/pkg/image/binary/registry.go @@ -28,17 +28,17 @@ type RegistryService struct { } func (*RegistryService) Login(domain, username, passwd string) error { - panic("implement me") + return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah login --username %s --password %s %s", username, passwd, domain)) } -func (*RegistryService) Logout(domain, username string) error { - panic("implement me") +func (*RegistryService) Logout(domain string) error { + return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah logout %s", domain)) } func (*RegistryService) Pull(image string) error { return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah pull %s", image)) } func (*RegistryService) Push(image string) error { - panic("implement me") + return exec.CmdForPipe("bash", "-c", fmt.Sprintf("buildah push %s", image)) } func (*RegistryService) Sync(localDir, imageName string) error { panic("implement me") diff --git a/pkg/image/types/interface.go b/pkg/image/types/interface.go index fc6d075cf..ec81446b4 100644 --- a/pkg/image/types/interface.go +++ b/pkg/image/types/interface.go @@ -20,19 +20,21 @@ import ( type RegistryService interface { Login(domain, username, passwd string) error - Logout(domain, username string) error + Logout(domain string) error Pull(image string) error Push(image string) error Sync(localDir, imageName string) error } type Service interface { - Rename(src, dst string) error - Remove(images ...string) error + Tag(src, dst string) error + Save(imageName, archiveName string) error + Load(archiveName string) error + Remove(force bool, images ...string) error Inspect(image string) (*v1.Image, error) //oci image - Build(options BuildOptions, contextDir, imageName string) error + Build(options *BuildOptions, contextDir, imageName string) error Prune() error - ListImages(opt ListOptions) ([]v1.Image, error) + ListImages() ([]ImageInfo, error) } type ClusterService interface { diff --git a/pkg/image/types/types.go b/pkg/image/types/types.go index 7ad8bee38..2611eac6e 100644 --- a/pkg/image/types/types.go +++ b/pkg/image/types/types.go @@ -16,10 +16,80 @@ limitations under the License. package types +import ( + "fmt" + "strings" + "time" +) + +type PullType string + +const ( + PullTypeIfMissing PullType = "false" + PullTypeIfNewer PullType = "true" + PullTypeAlways PullType = "always" + PullTypeNever PullType = "never" +) + type BuildOptions struct { + NoCache bool //--no-cache + AllPlatforms bool //--all-platforms + OS string //--os "linux" + Arch string //--arch "amd64" + DisableCompression bool //--disable-compression default true + File string //--file -f + ForceRemove bool //--force-rm + Remove bool //--rm true + Platform string //--platform linux/amd64 + Pull PullType //--pull string[="true"] true (true,always,never) + Tag string } -type ListOptions struct { +func (opts *BuildOptions) Default() { + opts.NoCache = false + opts.AllPlatforms = false + opts.DisableCompression = true + opts.ForceRemove = false + opts.Remove = true + opts.Pull = PullTypeIfNewer +} + +func (opts *BuildOptions) String() string { + var sb strings.Builder + if opts.NoCache { + sb.WriteString(" --no-cache ") + } + if opts.AllPlatforms { + sb.WriteString(" --all-platforms ") + } + if len(opts.OS) > 0 { + sb.WriteString(fmt.Sprintf(" --os %s ", opts.OS)) + } + if len(opts.Arch) > 0 { + sb.WriteString(fmt.Sprintf(" --arch %s ", opts.Arch)) + } + if !opts.DisableCompression { + sb.WriteString(" --disable-compression=false ") + } + if len(opts.File) > 0 { + sb.WriteString(fmt.Sprintf(" -f %s ", opts.File)) + } + if opts.ForceRemove { + sb.WriteString(" --force-rm ") + } + if !opts.Remove { + sb.WriteString(" --rm=false ") + } + if len(opts.Platform) > 0 { + sb.WriteString(fmt.Sprintf(" --platform %s ", opts.Platform)) + } + if len(opts.Pull) > 0 { + sb.WriteString(fmt.Sprintf(" --pull=%s ", string(opts.Pull))) + } + if len(opts.Tag) > 0 { + sb.WriteString(fmt.Sprintf(" -t %s ", opts.Tag)) + } + return sb.String() } type ClusterManifest struct { @@ -35,3 +105,15 @@ type ClusterInfo struct { Imagename string `json:"imagename"` Containername string `json:"containername"` } + +type ImageInfo struct { + ID string `json:"id"` + Names []string `json:"names"` + Digest string `json:"digest"` + CreatedAt string `json:"createdat"` + Size string `json:"size"` + Created int64 `json:"created"` + CreatedAtRaw time.Time `json:"createdatraw"` + ReadOnly bool `json:"readonly"` + History []string `json:"history"` +} diff --git a/pkg/registry/save_test.go b/pkg/registry/save_test.go index 57fdb5891..12127506d 100644 --- a/pkg/registry/save_test.go +++ b/pkg/registry/save_test.go @@ -19,10 +19,10 @@ import ( "reflect" "testing" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/distribution/distribution/v3/configuration" "github.com/docker/docker/api/types" - - v1 "github.com/opencontainers/image-spec/specs-go/v1" ) func TestSaveImages(t *testing.T) {