mirror of
https://github.com/labring/sealos.git
synced 2026-10-29 17:25:58 +08:00
delete cr build for buildah (#3953)
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user