diff --git a/airflow/mocks/PodmanBind.go b/airflow/mocks/PodmanBind.go index 710df0dab..b6d416af9 100644 --- a/airflow/mocks/PodmanBind.go +++ b/airflow/mocks/PodmanBind.go @@ -18,8 +18,6 @@ import ( play "github.com/containers/podman/v3/pkg/bindings/play" - podman "github.com/astronomer/astro-cli/pkg/podman" - pods "github.com/containers/podman/v3/pkg/bindings/pods" types "github.com/containers/image/v5/types" @@ -230,11 +228,11 @@ func (_m *PodmanBind) NewConnection(ctx context.Context, uri string) (context.Co } // Push provides a mock function with given fields: ctx, source, destination, options -func (_m *PodmanBind) Push(ctx context.Context, source string, destination string, options *podman.PushOptions) error { +func (_m *PodmanBind) Push(ctx context.Context, source string, destination string, options *images.PushOptions) error { ret := _m.Called(ctx, source, destination, options) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, *podman.PushOptions) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, string, *images.PushOptions) error); ok { r0 = rf(ctx, source, destination, options) } else { r0 = ret.Error(0) diff --git a/airflow/podman_bindings.go b/airflow/podman_bindings.go index 7dcb585c5..17f90d9e6 100644 --- a/airflow/podman_bindings.go +++ b/airflow/podman_bindings.go @@ -3,7 +3,6 @@ package airflow import ( "context" - podmanImages "github.com/astronomer/astro-cli/pkg/podman" "github.com/containers/common/pkg/auth" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/pkg/api/handlers" @@ -32,7 +31,7 @@ type PodmanBind interface { // Methods to handle image operations Build(ctx context.Context, containerFiles []string, options entities.BuildOptions) (*entities.BuildReport, error) Tag(ctx context.Context, nameOrID, tag, repo string, options *images.TagOptions) error - Push(ctx context.Context, source, destination string, options *podmanImages.PushOptions) error + Push(ctx context.Context, source, destination string, options *images.PushOptions) error Untag(ctx context.Context, nameOrID, tag, repo string, options *images.UntagOptions) error GetImage(ctx context.Context, nameOrID string, options *images.GetOptions) (*entities.ImageInspectReport, error) // Method to handle registry operations @@ -91,8 +90,8 @@ func (p *PodmanBinder) Tag(ctx context.Context, nameOrID, tag, repo string, opti return images.Tag(ctx, nameOrID, tag, repo, options) } -func (p *PodmanBinder) Push(ctx context.Context, source, destination string, options *podmanImages.PushOptions) error { - return podmanImages.Push(ctx, source, destination, options) +func (p *PodmanBinder) Push(ctx context.Context, source, destination string, options *images.PushOptions) error { + return images.Push(ctx, source, destination, options) } func (p *PodmanBinder) Untag(ctx context.Context, nameOrID, tag, repo string, options *images.UntagOptions) error { diff --git a/airflow/podman_image.go b/airflow/podman_image.go index e2d71a22e..ade72c6f1 100644 --- a/airflow/podman_image.go +++ b/airflow/podman_image.go @@ -6,10 +6,9 @@ import ( "os" "path/filepath" - podmanImages "github.com/astronomer/astro-cli/pkg/podman" - "github.com/containers/buildah" "github.com/containers/buildah/imagebuildah" + "github.com/containers/podman/v3/pkg/bindings/images" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" ) @@ -73,7 +72,7 @@ func (p *PodmanImage) Push(cloudDomain, token, remoteImageTag string) error { if err != nil { return errors.Wrapf(err, "command 'podman tag %s %s' failed", p.imageName, remoteImage) } - options := new(podmanImages.PushOptions).WithIdentityToken(token) + options := new(images.PushOptions) if err := p.podmanBind.Push(p.conn, p.imageName, remoteImage, options); err != nil { return errors.Wrapf(err, "Error pushing %s image to %s", p.imageName, registry) } diff --git a/pkg/podman/images.go b/pkg/podman/images.go deleted file mode 100644 index 85daf2eb0..000000000 --- a/pkg/podman/images.go +++ /dev/null @@ -1,116 +0,0 @@ -package podman - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "net/http" - "strconv" - - "github.com/containers/image/v5/types" - "github.com/containers/podman/v3/pkg/bindings" - dockerAPITypes "github.com/docker/docker/api/types" -) - -const ( - XRegistryAuthHeader = "X-Registry-Auth" -) - -// PushOptions are optional options for importing images -type PushOptions struct { - // All indicates whether to push all images related to the image list - All *bool - // Authfile is the path to the authentication file. Ignored for remote - // calls. - Authfile *string - // Compress tarball image layers when pushing to a directory using the 'dir' transport. - Compress *bool - // Manifest type of the pushed image - Format *string - // Password for authenticating against the registry. - Password *string - // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool - // Username for authenticating against the registry. - Username *string - // IdentityToken for authenticating against the registry. - IdentityToken *string -} - -// Push is the binding for libpod's v2 endpoints for push images. Note that -// `source` must be a referring to an image in the remote's container storage. -// The destination must be a reference to a registry (i.e., of docker transport -// or be normalized to one). Other transports are rejected as they do not make -// sense in a remote context. The binding is inline with podman's image binding -// package with an addition to allow the caller to pass the identity token as -// part of PushOptions. -func Push(ctx context.Context, source, destination string, options *PushOptions) error { - if options == nil { - options = new(PushOptions) - } - conn, err := bindings.GetClient(ctx) - if err != nil { - return err - } - // TODO: have a global system context we can pass around (1st argument) - header, err := authHeader(options.GetIdentityToken()) - if err != nil { - return err - } - - params, err := options.ToParams() - if err != nil { - return err - } - // SkipTLSVerify is special. We need to delete the param added by - // toparams and change the key and flip the bool - if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") - params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) - } - params.Set("destination", destination) - - path := fmt.Sprintf("/images/%s/push", source) - response, err := conn.DoRequest(nil, http.MethodPost, path, params, header) - if err != nil { - return err - } - defer response.Body.Close() - - return response.Process(err) -} - -func authHeader(identityToken string) (map[string]string, error) { - var ( - content string - err error - ) - content, err = encodeSingleAuthConfig(types.DockerAuthConfig{IdentityToken: identityToken}) - if err != nil { - return nil, err - } - - if len(content) > 0 { - return map[string]string{string(XRegistryAuthHeader): content}, nil - } - return nil, nil -} - -// encodeSingleAuthConfig serializes the auth configuration as a base64 encoded JSON payload. -func encodeSingleAuthConfig(authConfig types.DockerAuthConfig) (string, error) { - conf := imageAuthToDockerAuth(authConfig) - buf, err := json.Marshal(conf) - if err != nil { - return "", err - } - return base64.URLEncoding.EncodeToString(buf), nil -} - -func imageAuthToDockerAuth(authConfig types.DockerAuthConfig) dockerAPITypes.AuthConfig { - return dockerAPITypes.AuthConfig{ - Username: authConfig.Username, - Password: authConfig.Password, - IdentityToken: authConfig.IdentityToken, - } -} diff --git a/pkg/podman/types_push_options.go b/pkg/podman/types_push_options.go deleted file mode 100644 index ed20a33b1..000000000 --- a/pkg/podman/types_push_options.go +++ /dev/null @@ -1,34 +0,0 @@ -package podman - -import ( - "net/url" -) - -// ToParams formats struct fields to be passed to API service -func (o *PushOptions) ToParams() (url.Values, error) { - return ToParams(o) -} - -// GetSkipTLSVerify returns value of field SkipTLSVerify -func (o *PushOptions) GetSkipTLSVerify() bool { - if o.SkipTLSVerify == nil { - var z bool - return z - } - return *o.SkipTLSVerify -} - -// GetIdentityToken returns value of field IdentityToken -func (o *PushOptions) GetIdentityToken() string { - if o.IdentityToken == nil { - var z string - return z - } - return *o.IdentityToken -} - -// WithIdentityToken set field IdentityToken to given value -func (o *PushOptions) WithIdentityToken(token string) *PushOptions { - o.IdentityToken = &token - return o -} diff --git a/pkg/podman/utils.go b/pkg/podman/utils.go deleted file mode 100644 index 25df9a15d..000000000 --- a/pkg/podman/utils.go +++ /dev/null @@ -1,108 +0,0 @@ -package podman - -import ( - "errors" - "fmt" - "net/url" - "reflect" - "strconv" - "strings" - - jsoniter "github.com/json-iterator/go" -) - -var errInvalidTypeInSlice = errors.New("slices must contain only simple types") - -func IsSimpleType(f reflect.Value) bool { - if _, ok := f.Interface().(fmt.Stringer); ok { - return true - } - - switch f.Kind() { //nolint:exhaustive - case reflect.Bool, reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64, reflect.String: - return true - } - - return false -} - -func SimpleTypeToParam(f reflect.Value) string { - if s, ok := f.Interface().(fmt.Stringer); ok { - return s.String() - } - - switch f.Kind() { //nolint:exhaustive - case reflect.Bool: - return strconv.FormatBool(f.Bool()) - case reflect.Int, reflect.Int64: - // f.Int() is always an int64 - return strconv.FormatInt(f.Int(), 10) - case reflect.Uint, reflect.Uint64: - // f.Uint() is always an uint64 - return strconv.FormatUint(f.Uint(), 10) - case reflect.String: - return f.String() - } - - panic("the input parameter is not a simple type") -} - -func Changed(o interface{}, fieldName string) bool { - r := reflect.ValueOf(o) - value := reflect.Indirect(r).FieldByName(fieldName) - return !value.IsNil() -} - -func ToParams(o interface{}) (url.Values, error) { - params := url.Values{} - if o == nil || reflect.ValueOf(o).IsNil() { - return params, nil - } - json := jsoniter.ConfigCompatibleWithStandardLibrary - s := reflect.ValueOf(o) - if reflect.Ptr == s.Kind() { - s = s.Elem() - } - sType := s.Type() - for i := 0; i < s.NumField(); i++ { - fieldName := sType.Field(i).Name - if !Changed(o, fieldName) { - continue - } - fieldName = strings.ToLower(fieldName) - f := s.Field(i) - if reflect.Ptr == f.Kind() { - f = f.Elem() - } - paramName := fieldName - if pn, ok := sType.Field(i).Tag.Lookup("schema"); ok { - paramName = pn - } - switch { - case IsSimpleType(f): - params.Set(paramName, SimpleTypeToParam(f)) - case f.Kind() == reflect.Slice: - for i := 0; i < f.Len(); i++ { - elem := f.Index(i) - if IsSimpleType(elem) { - params.Add(paramName, SimpleTypeToParam(elem)) - } else { - return nil, errInvalidTypeInSlice - } - } - case f.Kind() == reflect.Map: - lowerCaseKeys := make(map[string]interface{}) - iter := f.MapRange() - for iter.Next() { - lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface() - } - s, err := json.MarshalToString(lowerCaseKeys) - if err != nil { - return nil, err - } - - params.Set(paramName, s) - } - } - return params, nil -}