delete cr build for buildah (#3953)

This commit is contained in:
yy
2023-09-18 14:57:01 +08:00
committed by GitHub
parent cbaf975a40
commit 22038a1999
4 changed files with 0 additions and 469 deletions
-135
View File
@@ -1,135 +0,0 @@
// Copyright © 2023 sealos.
//
// 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 buildah
import (
"errors"
"fmt"
"path/filepath"
"github.com/containers/image/v5/pkg/docker/config"
"github.com/spf13/cobra"
"k8s.io/client-go/util/homedir"
imagev1 "github.com/labring/sealos/controllers/imagehub/api/v1"
"github.com/labring/sealos/pkg/utils/file"
"github.com/labring/sealos/pkg/utils/logger"
)
// SpecificCroption is used to parse the cr-option `auto` to `yes` or `no`.
func SpecificCroption(args []string) CrOpionEnum {
var dest, username string
switch len(args) {
case 1:
dest = args[0]
case 2:
dest = args[1]
}
registry, err := parseRawURL(dest)
if err != nil {
logger.Debug("parse image url failed, skip image cr build")
return CrOptionNo
}
creds, err := config.GetAllCredentials(nil)
if err != nil {
logger.Debug("get all credentials failed, skip image cr build")
return CrOptionNo
}
if _, ok := creds[registry]; ok {
username = creds[registry].Username
} else {
logger.Debug("no credentials for this registry, skip image cr build")
return CrOptionNo
}
if !file.IsExist(filepath.Join(homedir.HomeDir(), SealosRootPath, registry)) ||
!file.IsExist(filepath.Join(homedir.HomeDir(), SealosRootPath, registry, username)) {
logger.Debug("no kubeconfig file for this registry, skip image cr build")
return CrOptionNo
}
return CrOptionYes
}
func NewImageCRBuilderFromArgs(cmd *cobra.Command, args []string) (*ImageCRBuilder, error) {
var dest string
switch len(args) {
case 1:
dest = args[0]
case 2:
dest = args[1]
default:
return nil, errors.New("dest image name must be specified")
}
store, err := getStore(cmd)
if err != nil {
return nil, err
}
registr, err := parseRawURL(dest)
if err != nil {
return nil, err
}
icb := &ImageCRBuilder{
image: dest,
store: store,
registry: registr,
imageCR: &imagev1.Image{},
}
return icb, nil
}
func NewAndRunImageCRBuilder(cmd *cobra.Command, args []string, iopts *pushOptions) error {
if iopts.crOption == CrOptionNo {
logger.Debug("skip image cr build by flag cr-option")
return nil
}
logger.Info("start image cr build and push")
icb, err := NewImageCRBuilderFromArgs(cmd, args)
if err != nil {
logger.Debug("new image cr builder failed, skip image cr build")
return err
}
// check if sealos registry, pahse registry info to ibc.
if !IsSealosRegistry(icb) {
logger.Debug("skip image cr build, not sealos registry")
return nil
}
if err := icb.Run(); err != nil {
logger.Error(err)
return err
}
logger.Info("image cr build and push success")
return nil
}
func IsSealosRegistry(icb *ImageCRBuilder) bool {
//Check Cri Login
creds, err := config.GetAllCredentials(nil)
if err != nil {
logger.Debug("get credentials err ,please login first ." + fmt.Sprintf("%v", err))
return false
}
if _, ok := creds[icb.registry]; ok {
icb.username = creds[icb.registry].Username
icb.userconfig = creds[icb.registry].Password
} else {
logger.Debug("get registry login info err, please login " + icb.registry + " first")
return false
}
if !file.IsExist(filepath.Join(homedir.HomeDir(), SealosRootPath, icb.registry)) ||
!file.IsExist(filepath.Join(homedir.HomeDir(), SealosRootPath, icb.registry, icb.username)) {
logger.Debug("no kubeconfig file for this registry, skip image cr build")
return false
}
return true
}
-250
View File
@@ -1,250 +0,0 @@
// Copyright © 2022 sealos.
//
// 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 buildah
import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"github.com/containers/storage"
"github.com/openconfig/gnmi/errlist"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"k8s.io/client-go/util/homedir"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
imagev1 "github.com/labring/sealos/controllers/imagehub/api/v1"
"github.com/labring/sealos/pkg/client-go/kubernetes"
"github.com/labring/sealos/pkg/utils/file"
"github.com/labring/sealos/pkg/utils/logger"
"github.com/labring/sealos/pkg/utils/rand"
)
type ImageCRBuilder struct {
image string
registry string
username string
userconfig string
imageCR *imagev1.Image
containerID string
mountPoint string
store storage.Store
}
func (icb *ImageCRBuilder) Run() error {
var pipe []func() error
pipe = append(
pipe,
icb.CreateContainer,
icb.CreatImageCR,
icb.MutateImageCR,
icb.ApplyImageCR,
icb.DeleteContainer,
)
for _, f := range pipe {
err := f()
if err != nil {
logger.Error(err)
return err
}
}
return nil
}
// CreateContainer create a container, return mountpoint and containerID
func (icb *ImageCRBuilder) CreateContainer() error {
logger.Debug("Executing CreateContainer in ImageCRBuilder")
// generate a random container name
containerID := fmt.Sprintf("%s-%s", icb.image, rand.Generator(8))
realImpl, err := New("")
if err != nil {
return err
}
builderInfo, err := realImpl.Create(containerID, icb.image)
if err != nil {
return err
}
if isExist := file.IsExist(builderInfo.MountPoint); !isExist {
return fmt.Errorf("mountPoint not Exist")
}
icb.containerID = containerID
icb.mountPoint = builderInfo.MountPoint
return nil
}
func (icb *ImageCRBuilder) CreatImageCR() error {
logger.Debug("Executing CreatImageCR in ImageCRBuilder")
if file.IsExist(filepath.Join(icb.mountPoint, ImageStdPath, ImageCRFile)) {
logger.Debug("image cr file exist, read it and parse it")
c, err := file.ReadAll(filepath.Join(icb.mountPoint, ImageStdPath, ImageCRFile))
if err != nil {
logger.Debug("read image cr file failed")
return err
}
err = icb.ParseImageCR(c)
if err != nil {
logger.Debug("parse image cr file failed")
return err
}
}
//if app base config not find
//image name which contains "/" and ":" couldn't be used in meta name
mateName := strings.Replace(icb.GetClearImagename(), ":", ".", -1)
mateName = strings.Replace(mateName, "/", ".", -1)
// replace image cr spec and matename by image
icb.imageCR.TypeMeta = metav1.TypeMeta{Kind: ImagehubKind, APIVersion: filepath.Join(ImagehubGroup, ImagehubVersion)}
icb.imageCR.ObjectMeta.Name = mateName
icb.imageCR.Spec.Name = imagev1.ImageName(icb.GetClearImagename())
logger.Debug("CreatImageCR in ImageCRBuilder done")
logger.Debug("ImageCRBuilder CreatImageCR result: ", icb.imageCR)
return nil
}
func (icb *ImageCRBuilder) MutateImageCR() error {
logger.Debug("Executing MutateImageCR in ImageCRBuilder")
var errl errlist.List
// Using a pipe helps with future file reading needs
fileReadPipeLine := []func() error{
//app config is the based file ,it should read first
icb.GetInspectInfo,
icb.GetReadmeDoc,
}
for _, f := range fileReadPipeLine {
if err := f(); err != nil {
errl.Add(err)
}
}
logger.Debug(errl.Err())
// Do not return error.
logger.Debug("MutateImageCR in ImageCRBuilder done")
logger.Debug("ImageCRBuilder MutateImageCR result: ", icb.imageCR)
return nil
}
func (icb *ImageCRBuilder) ApplyImageCR() error {
logger.Debug("Executing ApplyImageCR in ImageCRBuilder")
client, err := kubernetes.NewKubernetesClient(filepath.Join(homedir.HomeDir(), SealosRootPath, icb.registry, icb.username, KubeConfigPath), "")
if err != nil {
return err
}
resource := client.KubernetesDynamic().Resource(schema.GroupVersionResource{
Group: ImagehubGroup,
Version: ImagehubVersion,
Resource: ImagehubResource,
})
utd := unstructured.Unstructured{}
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(icb.imageCR)
if err != nil {
return err
}
utd.Object = obj
if _, err = resource.Create(context.TODO(), &utd, metav1.CreateOptions{}); err != nil {
if !apierrors.IsAlreadyExists(err) {
return err
}
if utd.GetResourceVersion() == "" {
var objGet *unstructured.Unstructured
objGet, err = resource.Get(context.TODO(), utd.GetName(), metav1.GetOptions{})
if err != nil {
return err
}
utd.SetResourceVersion(objGet.GetResourceVersion())
}
if _, err = resource.Update(context.TODO(), &utd, metav1.UpdateOptions{}); err != nil {
return err
}
}
return nil
}
func (icb *ImageCRBuilder) DeleteContainer() error {
logger.Debug("Executing DeleteContainer in ImageCRBuilder")
realImpl, err := New("")
if err != nil {
return err
}
return realImpl.Delete(icb.containerID)
}
// ParseImageCR parse image cr yaml file ot an image cr obj
func (icb *ImageCRBuilder) ParseImageCR(c []byte) error {
logger.Debug("Executing ParseImageCR in ImageCRBuilder")
cr := imagev1.Image{}
_, _, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(c, nil, &cr)
if err != nil {
return fmt.Errorf("configParse err : %v", err)
}
cr.Spec.DetailInfo.Actions = make(map[string]imagev1.Action)
icb.imageCR = &cr
return nil
}
// GetReadmeDoc gen image cr detail info: readme
func (icb *ImageCRBuilder) GetReadmeDoc() error {
logger.Debug("Executing GetReadmeDoc in ImageCRBuilder")
if file.IsExist(filepath.Join(icb.mountPoint, DocsPath)) {
c, err := file.ReadAll(filepath.Join(icb.mountPoint, DocsPath))
if err != nil {
return err
}
icb.imageCR.Spec.DetailInfo.Docs = string(c)
}
return nil
}
// GetInspectInfo gen image cr detail info: Id, Arch and Size
func (icb *ImageCRBuilder) GetInspectInfo() error {
realImpl, err := New("")
if err != nil {
return err
}
containerInfo, err := realImpl.InspectContainer(icb.containerID)
if err != nil {
return err
}
var image v1.Image
if err = json.Unmarshal([]byte(containerInfo.Config), &image); err != nil {
return err
}
var manifest v1.Manifest
if err = json.Unmarshal([]byte(containerInfo.Manifest), &manifest); err != nil {
return err
}
sz, err := icb.store.ImageSize(containerInfo.FromImageID)
if err != nil {
return err
}
icb.imageCR.Spec.DetailInfo.ID = containerInfo.FromImageID
icb.imageCR.Spec.DetailInfo.Arch = image.Architecture
icb.imageCR.Spec.DetailInfo.Size = sz
icb.imageCR.Spec.DetailInfo.CreateTime = metav1.Time{Time: *image.Created}
return nil
}
// GetClearImagename replace 'hub.sealos.cn/org/repo:tag' to 'org/repo:tag'
func (icb *ImageCRBuilder) GetClearImagename() string {
return strings.Replace(icb.image, icb.registry+"/", "", 1)
}
-63
View File
@@ -1,63 +0,0 @@
// Copyright © 2023 sealos.
//
// 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 buildah
import "errors"
const (
// DocsPath ImageStdPath ImageCRFile are path to image cr and readme
DocsPath = "README.md"
ImageStdPath = "metadata"
// ImageCRFile Todo how can we support both config.yml and config.yaml
ImageCRFile = "config.yaml"
// SealosRootPath and KubeConfigPath is path to user kubeconfig
SealosRootPath = ".sealos"
KubeConfigPath = "config"
// ImagehubGroup ImagehubVersion ImagehubKind are group version kind of image cr
ImagehubGroup = "imagehub.sealos.io"
ImagehubVersion = "v1"
ImagehubKind = "Image"
ImagehubResource = "images"
)
type CrOpionEnum string
const (
CrOptionNo CrOpionEnum = "no"
CrOptionYes CrOpionEnum = "yes"
CrOptionOnly CrOpionEnum = "only"
CrOptionAuto CrOpionEnum = "auto"
)
// String is used both by fmt.Print and by Cobra in help text
func (e *CrOpionEnum) String() string {
return string(*e)
}
func (e *CrOpionEnum) Set(v string) error {
switch v {
case "only", "yes", "no", "auto":
*e = CrOpionEnum(v)
return nil
default:
return errors.New(`must be one of "only", "yes", "no", "auto"`)
}
}
func (e *CrOpionEnum) Type() string {
return "image cr opion enum"
}
-21
View File
@@ -62,9 +62,6 @@ type pushOptions struct {
encryptionKeys []string
encryptLayers []int
insecure bool
// TODO: remove this flag once we have a better way to handle image cr pushing to the dest registry
crOption CrOpionEnum
}
func newDefaultPushOptions() *pushOptions {
@@ -72,7 +69,6 @@ func newDefaultPushOptions() *pushOptions {
authfile: auth.GetDefaultAuthFile(),
retry: buildahcli.MaxPullPushRetries,
retryDelay: buildahcli.PullPushRetryDelay,
crOption: CrOptionAuto,
}
}
@@ -99,8 +95,6 @@ func (opts *pushOptions) RegisterFlags(fs *pflag.FlagSet) error {
fs.IntSliceVar(&opts.encryptLayers, "encrypt-layer", opts.encryptLayers, "layers to encrypt, 0-indexed layer indices with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer). If not defined, will encrypt all layers if encryption-key flag is specified")
fs.BoolVar(&opts.tlsVerify, "tls-verify", opts.tlsVerify, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
// TODO: remove this flag once we have a better way to handle image cr pushing to the dest registry
fs.Var(&opts.crOption, "cr-option", `push image cr to the dest registry, allow values: "yes", "no", "only", "auto"`)
return markFlagsHidden(fs, []string{"signature-policy", "blob-cache", "tls-verify"}...)
}
@@ -121,18 +115,9 @@ func newPushCommand() *cobra.Command {
Use: "push",
Short: "Push an image to a specified destination",
Long: pushDescription,
PreRun: func(cmd *cobra.Command, args []string) {
// specific croption is only valid when crOption is auto, and switch to a specific croption `yes` / `no`
if opts.crOption == CrOptionAuto {
opts.crOption = SpecificCroption(args)
}
},
RunE: func(cmd *cobra.Command, args []string) error {
return pushCmd(cmd, args, opts)
},
PostRunE: func(cmd *cobra.Command, args []string) error {
return NewAndRunImageCRBuilder(cmd, args, opts)
},
Example: fmt.Sprintf(`%[1]s push imageID docker://registry.example.com/repository:tag
%[1]s push imageID docker-daemon:image:tagi
%[1]s push imageID oci:/path/to/layout:image:tag`, rootCmd.CommandPath()),
@@ -255,12 +240,6 @@ func pushCmd(c *cobra.Command, args []string, iopts *pushOptions) error {
options.CompressionLevel = &iopts.compressionLevel
}
// check if the cr option is set to only and return before push.
if iopts.crOption == CrOptionOnly {
logger.Info("Only push image cr to the dest registry")
return nil
}
ref, digest, err := buildah.Push(getContext(), src, dest, options)
if err != nil {
if !errors.Is(err, storage.ErrImageUnknown) {