diff --git a/go.mod b/go.mod index 4eff60bf8..6bfa907f7 100644 --- a/go.mod +++ b/go.mod @@ -71,6 +71,7 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-containerregistry v0.19.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect @@ -95,6 +96,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/onsi/gomega v1.31.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opensearch-project/opensearch-go v1.1.0 // indirect github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect diff --git a/go.sum b/go.sum index d6c3ebb39..6cc6c0fed 100644 --- a/go.sum +++ b/go.sum @@ -186,6 +186,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.19.0 h1:uIsMRBV7m/HDkDxE/nXMnv1q+lOOSPlQ/ywc5JbB8Ic= +github.com/google/go-containerregistry v0.19.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -324,6 +326,8 @@ github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M= github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo= github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ= diff --git a/pkg/cmds/pause.go b/pkg/cmds/pause.go index 77f1117fc..f3b0cf829 100644 --- a/pkg/cmds/pause.go +++ b/pkg/cmds/pause.go @@ -74,8 +74,9 @@ type PauseOptions struct { genericclioptions.IOStreams - onlyDb bool - onlyBackup bool + onlyDb bool + onlyBackup bool + onlyArchiver bool } func NewCmdPause(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { @@ -106,6 +107,7 @@ func NewCmdPause(parent string, f cmdutil.Factory, streams genericclioptions.IOS cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().BoolVar(&o.onlyDb, "only-db", false, "If provided, only the database is paused.") cmd.Flags().BoolVar(&o.onlyBackup, "only-backupconfig", false, "If provided, only the backupconfiguration for the database is paused.") + cmd.Flags().BoolVar(&o.onlyArchiver, "only-archiver", false, "If provided, only the archiver for the database is paused.") return cmd } @@ -165,7 +167,7 @@ func (o *PauseOptions) Run() error { errs := sets.NewString() for _, info := range infos { - psr, err := pauser.NewPauser(o.Factory, info.Mapping, o.onlyDb, o.onlyBackup) + psr, err := pauser.NewPauser(o.Factory, info.Mapping, o.onlyDb, o.onlyBackup, o.onlyArchiver) if err != nil { if errs.Has(err.Error()) { continue @@ -181,8 +183,9 @@ func (o *PauseOptions) Run() error { } allErrs = append(allErrs, err) errs.Insert(err.Error()) + continue } - pauseAll := !(o.onlyBackup || o.onlyDb) + pauseAll := !(o.onlyBackup || o.onlyDb || o.onlyArchiver) if o.onlyDb || pauseAll { fmt.Fprintf(o.Out, "Successfully paused %s/%s.\n", info.Namespace, info.Name) @@ -190,6 +193,9 @@ func (o *PauseOptions) Run() error { if (o.onlyBackup || pauseAll) && backupConfigFound { fmt.Fprintf(o.Out, "Successfully paused backupconfigurations of %s/%s.\n", info.Namespace, info.Name) } + if o.onlyArchiver || pauseAll { + fmt.Fprintf(o.Out, "Successfully paused archiver of db %s/%s.\n", info.Namespace, info.Name) + } } return utilerrors.NewAggregate(allErrs) diff --git a/pkg/cmds/resume.go b/pkg/cmds/resume.go index 6f6421e4e..98fce409e 100644 --- a/pkg/cmds/resume.go +++ b/pkg/cmds/resume.go @@ -74,8 +74,9 @@ type ResumeOptions struct { genericclioptions.IOStreams - onlyDb bool - onlyBackup bool + onlyDb bool + onlyBackup bool + onlyArchiver bool } func NewCmdResume(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { @@ -105,6 +106,7 @@ func NewCmdResume(parent string, f cmdutil.Factory, streams genericclioptions.IO cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().BoolVar(&o.onlyDb, "only-db", false, "If provided, only the database is resumed.") cmd.Flags().BoolVar(&o.onlyBackup, "only-backupconfig", false, "If provided, only the backupconfiguration for the database is resumed.") + cmd.Flags().BoolVar(&o.onlyArchiver, "only-archiver", false, "If provided, only the archiver for the database is resumed.") return cmd } @@ -164,7 +166,7 @@ func (o *ResumeOptions) Run() error { errs := sets.NewString() for _, info := range infos { - rsr, err := resumer.NewResumer(o.Factory, info.Mapping, o.onlyDb, o.onlyBackup) + rsr, err := resumer.NewResumer(o.Factory, info.Mapping, o.onlyDb, o.onlyBackup, o.onlyArchiver) if err != nil { if errs.Has(err.Error()) { continue @@ -182,7 +184,7 @@ func (o *ResumeOptions) Run() error { errs.Insert(err.Error()) } - resumeAll := !(o.onlyBackup || o.onlyDb) + resumeAll := !(o.onlyBackup || o.onlyDb || o.onlyArchiver) if o.onlyDb || resumeAll { fmt.Fprintf(o.Out, "Successfully resumed %s/%s.\n", info.Namespace, info.Name) @@ -190,6 +192,9 @@ func (o *ResumeOptions) Run() error { if (o.onlyBackup || resumeAll) && backupConfigFound { fmt.Fprintf(o.Out, "Successfully resumed backupconfigurations of %s/%s.\n", info.Namespace, info.Name) } + if o.onlyArchiver || resumeAll { + fmt.Fprintf(o.Out, "Successfully resumed archiver of db %s/%s.\n", info.Namespace, info.Name) + } } return utilerrors.NewAggregate(allErrs) } diff --git a/pkg/data/mariadb.go b/pkg/data/mariadb.go index a343546e1..871bf8baf 100644 --- a/pkg/data/mariadb.go +++ b/pkg/data/mariadb.go @@ -323,9 +323,9 @@ func (opts *mariadbOpts) getShellCommand(command string) (string, error) { containerName := "mariadb" if db.Spec.TLS != nil { - cmd = fmt.Sprintf("kubectl exec -n %s svc/%s -c %s -- mysql -u%s -p'%s' --host=%s --port=%s --ssl-ca='%v' --ssl-cert='%v' --ssl-key='%v' %s -e \"%s\"", db.Namespace, db.OffshootName(), containerName, user, password, "127.0.0.1", "3306", mdCaFile, mdCertFile, mdKeyFile, api.ResourceSingularMySQL, command) + cmd = fmt.Sprintf("kubectl exec -n %s svc/%s -c %s -- mariadb -u%s -p'%s' --host=%s --port=%s --ssl-ca='%v' --ssl-cert='%v' --ssl-key='%v' %s -e \"%s\"", db.Namespace, db.OffshootName(), containerName, user, password, "127.0.0.1", "3306", mdCaFile, mdCertFile, mdKeyFile, api.ResourceSingularMySQL, command) } else { - cmd = fmt.Sprintf("kubectl exec -n %s svc/%s -c %s -- mysql -u%s -p'%s' %s -e \"%s\"", db.Namespace, db.OffshootName(), containerName, user, password, api.ResourceSingularMySQL, command) + cmd = fmt.Sprintf("kubectl exec -n %s svc/%s -c %s -- mariadb -u%s -p'%s' %s -e \"%s\"", db.Namespace, db.OffshootName(), containerName, user, password, api.ResourceSingularMySQL, command) } return cmd, err diff --git a/pkg/pauser/archiver.go b/pkg/pauser/archiver.go new file mode 100644 index 000000000..964b9f6e9 --- /dev/null +++ b/pkg/pauser/archiver.go @@ -0,0 +1,173 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the AppsCode Community License 1.0.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://github.com/appscode/licenses/raw/1.0.0/AppsCode-Community-1.0.0.md + +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 pauser + +import ( + "context" + + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kmapi "kmodules.xyz/client-go/api/v1" + kmc "kmodules.xyz/client-go/client" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func PauseOrResumeMySQLArchiver(klient client.Client, value bool, reference kmapi.ObjectReference) error { + name := reference.Name + namespace := reference.Namespace + archiver, err := getMysqlArchiver(klient, kmapi.ObjectReference{ + Name: name, + Namespace: namespace, + }) + if err != nil { + return err + } + _, err = kmc.CreateOrPatch( + context.Background(), + klient, + archiver, + func(obj client.Object, createOp bool) client.Object { + in := obj.(*coreapi.MySQLArchiver) + in.Spec.Pause = value + return in + }, + ) + return err +} + +func getMysqlArchiver(klient client.Client, ref kmapi.ObjectReference) (*coreapi.MySQLArchiver, error) { + archiver := &coreapi.MySQLArchiver{ + ObjectMeta: metav1.ObjectMeta{ + Name: ref.Name, + Namespace: ref.Namespace, + }, + } + if err := klient.Get(context.Background(), client.ObjectKeyFromObject(archiver), archiver); err != nil { + return nil, err + } + return archiver, nil +} + +func PauseOrResumeMariaDBArchiver(klient client.Client, value bool, reference kmapi.ObjectReference) error { + name := reference.Name + namespace := reference.Namespace + archiver, err := getMariaDBArchiver(klient, kmapi.ObjectReference{ + Name: name, + Namespace: namespace, + }) + if err != nil { + return err + } + _, err = kmc.CreateOrPatch( + context.Background(), + klient, + archiver, + func(obj client.Object, createOp bool) client.Object { + in := obj.(*coreapi.MariaDBArchiver) + in.Spec.Pause = value + return in + }, + ) + return err +} + +func getMariaDBArchiver(klient client.Client, ref kmapi.ObjectReference) (*coreapi.MariaDBArchiver, error) { + archiver := &coreapi.MariaDBArchiver{ + ObjectMeta: metav1.ObjectMeta{ + Name: ref.Name, + Namespace: ref.Namespace, + }, + } + if err := klient.Get(context.Background(), client.ObjectKeyFromObject(archiver), archiver); err != nil { + return nil, err + } + + return archiver, nil +} + +func PauseOrResumePostgresArchiver(klient client.Client, value bool, reference kmapi.ObjectReference) error { + name := reference.Name + namespace := reference.Namespace + archiver, err := getPostgresArchiver(klient, kmapi.ObjectReference{ + Name: name, + Namespace: namespace, + }) + if err != nil { + return err + } + _, err = kmc.CreateOrPatch( + context.Background(), + klient, + archiver, + func(obj client.Object, createOp bool) client.Object { + in := obj.(*coreapi.PostgresArchiver) + in.Spec.Pause = value + return in + }, + ) + return err +} + +func getPostgresArchiver(klient client.Client, ref kmapi.ObjectReference) (*coreapi.PostgresArchiver, error) { + archiver := &coreapi.PostgresArchiver{ + ObjectMeta: metav1.ObjectMeta{ + Name: ref.Name, + Namespace: ref.Namespace, + }, + } + if err := klient.Get(context.Background(), client.ObjectKeyFromObject(archiver), archiver); err != nil { + return nil, err + } + return archiver, nil +} + +func PauseOrResumeMongoDBArchiver(klient client.Client, value bool, reference kmapi.ObjectReference) error { + name := reference.Name + namespace := reference.Namespace + archiver, err := getMongoDBArchiver(klient, kmapi.ObjectReference{ + Name: name, + Namespace: namespace, + }) + if err != nil { + return err + } + _, err = kmc.CreateOrPatch( + context.Background(), + klient, + archiver, + func(obj client.Object, createOp bool) client.Object { + in := obj.(*coreapi.MongoDBArchiver) + in.Spec.Pause = value + return in + }, + ) + return err +} + +func getMongoDBArchiver(klient client.Client, ref kmapi.ObjectReference) (*coreapi.MongoDBArchiver, error) { + archiver := &coreapi.MongoDBArchiver{ + ObjectMeta: metav1.ObjectMeta{ + Name: ref.Name, + Namespace: ref.Namespace, + }, + } + if err := klient.Get(context.Background(), client.ObjectKeyFromObject(archiver), archiver); err != nil { + return nil, err + } + return archiver, nil +} diff --git a/pkg/pauser/mariadb.go b/pkg/pauser/mariadb.go index cf2f94531..119cf87f0 100644 --- a/pkg/pauser/mariadb.go +++ b/pkg/pauser/mariadb.go @@ -19,6 +19,7 @@ package pauser import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" @@ -26,18 +27,22 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type MariaDBPauser struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewMariaDBPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MariaDBPauser, error) { +func NewMariaDBPauser(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*MariaDBPauser, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -48,11 +53,18 @@ func NewMariaDBPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*Mari return nil, err } + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } + return &MariaDBPauser{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -62,7 +74,15 @@ func (e *MariaDBPauser) Pause(name, namespace string) (bool, error) { return false, nil } - pauseAll := !(e.onlyBackup || e.onlyDb) + pauseAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + if e.onlyArchiver || pauseAll { + if err := PauseOrResumeMariaDBArchiver(e.kc, true, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || pauseAll { _, err = dbutil.UpdateMariaDBStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.MariaDBStatus) (types.UID, *api.MariaDBStatus) { diff --git a/pkg/pauser/mongodb.go b/pkg/pauser/mongodb.go index 724462946..013fdb2aa 100644 --- a/pkg/pauser/mongodb.go +++ b/pkg/pauser/mongodb.go @@ -19,6 +19,7 @@ package pauser import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" @@ -26,18 +27,22 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type MongoDBPauser struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewMongoDBPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MongoDBPauser, error) { +func NewMongoDBPauser(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*MongoDBPauser, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -48,11 +53,18 @@ func NewMongoDBPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*Mong return nil, err } + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } + return &MongoDBPauser{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -62,7 +74,15 @@ func (e *MongoDBPauser) Pause(name, namespace string) (bool, error) { return false, nil } - pauseAll := !(e.onlyBackup || e.onlyDb) + pauseAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + if e.onlyArchiver || pauseAll { + if err := PauseOrResumeMongoDBArchiver(e.kc, true, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || pauseAll { _, err = dbutil.UpdateMongoDBStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.MongoDBStatus) (types.UID, *api.MongoDBStatus) { diff --git a/pkg/pauser/mysql.go b/pkg/pauser/mysql.go index 5dff27827..6f6f7bd67 100644 --- a/pkg/pauser/mysql.go +++ b/pkg/pauser/mysql.go @@ -19,6 +19,7 @@ package pauser import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" @@ -26,18 +27,22 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type MySQLPauser struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewMySQLPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MySQLPauser, error) { +func NewMySQLPauser(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*MySQLPauser, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -48,22 +53,35 @@ func NewMySQLPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MySQLP return nil, err } + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } + return &MySQLPauser{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } -func (e *MySQLPauser) Pause(name, namespace string) (bool, error) { +func (e *MySQLPauser) Pause(name string, namespace string) (bool, error) { db, err := e.dbClient.MySQLs(namespace).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { return false, nil } - - pauseAll := !(e.onlyBackup || e.onlyDb) - + pauseAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + if e.onlyArchiver || pauseAll { + if err := PauseOrResumeMySQLArchiver(e.kc, true, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || pauseAll { _, err = dbutil.UpdateMySQLStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.MySQLStatus) (types.UID, *api.MySQLStatus) { status.Conditions = condutil.SetCondition(status.Conditions, condutil.NewCondition( @@ -77,10 +95,8 @@ func (e *MySQLPauser) Pause(name, namespace string) (bool, error) { return false, nil } } - if e.onlyBackup || pauseAll { return PauseBackupConfiguration(e.stashClient, db.ObjectMeta) } - return false, nil } diff --git a/pkg/pauser/pauser.go b/pkg/pauser/pauser.go index b83a078fc..ec97722e7 100644 --- a/pkg/pauser/pauser.go +++ b/pkg/pauser/pauser.go @@ -29,7 +29,7 @@ type Pauser interface { Pause(string, string) (bool, error) // returns true if backupconfiguration is paused } -func NewPauser(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping, onlyDb, onlyBackup bool) (Pauser, error) { +func NewPauser(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping, onlyDb, onlyBackup, onlyArchiver bool) (Pauser, error) { clientConfig, err := restClientGetter.ToRESTConfig() if err != nil { return nil, err @@ -42,13 +42,13 @@ func NewPauser(restClientGetter genericclioptions.RESTClientGetter, mapping *met case api.ResourceKindElasticsearch: return NewElasticsearchPauser(clientConfig, onlyDb, onlyBackup) case api.ResourceKindMongoDB: - return NewMongoDBPauser(clientConfig, onlyDb, onlyBackup) + return NewMongoDBPauser(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindMySQL: - return NewMySQLPauser(clientConfig, onlyDb, onlyBackup) + return NewMySQLPauser(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindMariaDB: - return NewMariaDBPauser(clientConfig, onlyDb, onlyBackup) + return NewMariaDBPauser(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindPostgres: - return NewPostgresPauser(clientConfig, onlyDb, onlyBackup) + return NewPostgresPauser(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindRedis: return NewRedisPauser(clientConfig, onlyDb, onlyBackup) default: diff --git a/pkg/pauser/postgres.go b/pkg/pauser/postgres.go index 730cf8aef..b868c766e 100644 --- a/pkg/pauser/postgres.go +++ b/pkg/pauser/postgres.go @@ -19,6 +19,7 @@ package pauser import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" @@ -26,18 +27,22 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type PostgresPauser struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewPostgresPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*PostgresPauser, error) { +func NewPostgresPauser(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*PostgresPauser, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -48,11 +53,18 @@ func NewPostgresPauser(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*Pos return nil, err } + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } + return &PostgresPauser{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -62,7 +74,16 @@ func (e *PostgresPauser) Pause(name, namespace string) (bool, error) { return false, nil } - pauseAll := !(e.onlyBackup || e.onlyDb) + pauseAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + + if e.onlyArchiver || pauseAll { + if err := PauseOrResumePostgresArchiver(e.kc, true, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || pauseAll { _, err = dbutil.UpdatePostgresStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.PostgresStatus) (types.UID, *api.PostgresStatus) { diff --git a/pkg/pauser/util.go b/pkg/pauser/util.go index 211c02a73..bc9757a51 100644 --- a/pkg/pauser/util.go +++ b/pkg/pauser/util.go @@ -53,6 +53,5 @@ func PauseBackupConfiguration(stashClient scs.StashV1beta1Interface, dbMeta meta return false, err } } - return dbBackupConfig != nil, nil } diff --git a/pkg/resumer/mariadb.go b/pkg/resumer/mariadb.go index b6baac212..2cf28a01f 100644 --- a/pkg/resumer/mariadb.go +++ b/pkg/resumer/mariadb.go @@ -19,26 +19,32 @@ package resumer import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" + pautil "kubedb.dev/cli/pkg/pauser" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type MariaDBResumer struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewMariaDBResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MariaDBResumer, error) { +func NewMariaDBResumer(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*MariaDBResumer, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -47,12 +53,17 @@ func NewMariaDBResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*Mar if err != nil { return nil, err } - + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } return &MariaDBResumer{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -62,7 +73,16 @@ func (e *MariaDBResumer) Resume(name, namespace string) (bool, error) { return false, err } - resumeAll := !(e.onlyBackup || e.onlyDb) + resumeAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + + if e.onlyArchiver || resumeAll { + if err := pautil.PauseOrResumeMariaDBArchiver(e.kc, false, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || resumeAll { _, err = dbutil.UpdateMariaDBStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.MariaDBStatus) (types.UID, *api.MariaDBStatus) { diff --git a/pkg/resumer/mongodb.go b/pkg/resumer/mongodb.go index 94f60f163..fbfc7215e 100644 --- a/pkg/resumer/mongodb.go +++ b/pkg/resumer/mongodb.go @@ -19,26 +19,32 @@ package resumer import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" + pautil "kubedb.dev/cli/pkg/pauser" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type MongoDBResumer struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewMongoDBResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MongoDBResumer, error) { +func NewMongoDBResumer(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*MongoDBResumer, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -48,11 +54,18 @@ func NewMongoDBResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*Mon return nil, err } + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } + return &MongoDBResumer{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -61,7 +74,16 @@ func (e *MongoDBResumer) Resume(name, namespace string) (bool, error) { if err != nil { return false, err } - resumeAll := !(e.onlyBackup || e.onlyDb) + resumeAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + + if e.onlyArchiver || resumeAll { + if err := pautil.PauseOrResumeMongoDBArchiver(e.kc, false, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || resumeAll { _, err = dbutil.UpdateMongoDBStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.MongoDBStatus) (types.UID, *api.MongoDBStatus) { diff --git a/pkg/resumer/mysql.go b/pkg/resumer/mysql.go index cd51bfd31..5f606da2e 100644 --- a/pkg/resumer/mysql.go +++ b/pkg/resumer/mysql.go @@ -19,26 +19,32 @@ package resumer import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" + pautil "kubedb.dev/cli/pkg/pauser" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type MySQLResumer struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewMySQLResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MySQLResumer, error) { +func NewMySQLResumer(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*MySQLResumer, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -47,12 +53,17 @@ func NewMySQLResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*MySQL if err != nil { return nil, err } - + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } return &MySQLResumer{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -62,7 +73,16 @@ func (e *MySQLResumer) Resume(name, namespace string) (bool, error) { return false, err } - resumeAll := !(e.onlyBackup || e.onlyDb) + resumeAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + + if e.onlyArchiver || resumeAll { + if err := pautil.PauseOrResumeMySQLArchiver(e.kc, false, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || resumeAll { _, err = dbutil.UpdateMySQLStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.MySQLStatus) (types.UID, *api.MySQLStatus) { diff --git a/pkg/resumer/postgres.go b/pkg/resumer/postgres.go index ab9f6d91e..2559b7e26 100644 --- a/pkg/resumer/postgres.go +++ b/pkg/resumer/postgres.go @@ -19,26 +19,32 @@ package resumer import ( "context" + coreapi "kubedb.dev/apimachinery/apis/archiver/v1alpha1" api "kubedb.dev/apimachinery/apis/kubedb/v1alpha2" cs "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2" dbutil "kubedb.dev/apimachinery/client/clientset/versioned/typed/kubedb/v1alpha2/util" + pautil "kubedb.dev/cli/pkg/pauser" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/rest" + kmc "kmodules.xyz/client-go/client" condutil "kmodules.xyz/client-go/conditions" + "sigs.k8s.io/controller-runtime/pkg/client" scs "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1" ) type PostgresResumer struct { - dbClient cs.KubedbV1alpha2Interface - stashClient scs.StashV1beta1Interface - onlyDb bool - onlyBackup bool + dbClient cs.KubedbV1alpha2Interface + stashClient scs.StashV1beta1Interface + kc client.Client + onlyDb bool + onlyBackup bool + onlyArchiver bool } -func NewPostgresResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*PostgresResumer, error) { +func NewPostgresResumer(clientConfig *rest.Config, onlyDb, onlyBackup, onlyArchiver bool) (*PostgresResumer, error) { dbClient, err := cs.NewForConfig(clientConfig) if err != nil { return nil, err @@ -48,11 +54,18 @@ func NewPostgresResumer(clientConfig *rest.Config, onlyDb, onlyBackup bool) (*Po return nil, err } + kc, err := kmc.NewUncachedClient(clientConfig, coreapi.AddToScheme) + if err != nil { + return nil, err + } + return &PostgresResumer{ - dbClient: dbClient, - stashClient: stashClient, - onlyDb: onlyDb, - onlyBackup: onlyBackup, + dbClient: dbClient, + stashClient: stashClient, + kc: kc, + onlyDb: onlyDb, + onlyBackup: onlyBackup, + onlyArchiver: onlyArchiver, }, nil } @@ -61,7 +74,16 @@ func (e *PostgresResumer) Resume(name, namespace string) (bool, error) { if err != nil { return false, err } - resumeAll := !(e.onlyBackup || e.onlyDb) + resumeAll := !(e.onlyBackup || e.onlyDb || e.onlyArchiver) + + if e.onlyArchiver || resumeAll { + if err := pautil.PauseOrResumePostgresArchiver(e.kc, false, db.Spec.Archiver.Ref); err != nil { + return false, err + } + if e.onlyArchiver { + return false, nil + } + } if e.onlyDb || resumeAll { _, err = dbutil.UpdatePostgresStatus(context.TODO(), e.dbClient, db.ObjectMeta, func(status *api.PostgresStatus) (types.UID, *api.PostgresStatus) { diff --git a/pkg/resumer/resumer.go b/pkg/resumer/resumer.go index e81da783c..6292e6d18 100644 --- a/pkg/resumer/resumer.go +++ b/pkg/resumer/resumer.go @@ -29,7 +29,7 @@ type Resumer interface { Resume(string, string) (bool, error) // returns true if backupconfiguration is resumed } -func NewResumer(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping, onlyDb, onlyBackup bool) (Resumer, error) { +func NewResumer(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping, onlyDb, onlyBackup, onlyArchiver bool) (Resumer, error) { clientConfig, err := restClientGetter.ToRESTConfig() if err != nil { return nil, err @@ -42,13 +42,13 @@ func NewResumer(restClientGetter genericclioptions.RESTClientGetter, mapping *me case api.ResourceKindElasticsearch: return NewElasticsearchResumer(clientConfig, onlyDb, onlyBackup) case api.ResourceKindMongoDB: - return NewMongoDBResumer(clientConfig, onlyDb, onlyBackup) + return NewMongoDBResumer(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindMySQL: - return NewMySQLResumer(clientConfig, onlyDb, onlyBackup) + return NewMySQLResumer(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindMariaDB: - return NewMariaDBResumer(clientConfig, onlyDb, onlyBackup) + return NewMariaDBResumer(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindPostgres: - return NewPostgresResumer(clientConfig, onlyDb, onlyBackup) + return NewPostgresResumer(clientConfig, onlyDb, onlyBackup, onlyArchiver) case api.ResourceKindRedis: return NewRedisResumer(clientConfig, onlyDb, onlyBackup) default: diff --git a/vendor/github.com/google/go-containerregistry/LICENSE b/vendor/github.com/google/go-containerregistry/LICENSE new file mode 100644 index 000000000..7a4a3ea24 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/README.md b/vendor/github.com/google/go-containerregistry/pkg/name/README.md new file mode 100644 index 000000000..4889b8446 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/README.md @@ -0,0 +1,3 @@ +# `name` + +[![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/name?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/name) diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/check.go b/vendor/github.com/google/go-containerregistry/pkg/name/check.go new file mode 100644 index 000000000..e9a240a3e --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/check.go @@ -0,0 +1,43 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + "strings" + "unicode/utf8" +) + +// stripRunesFn returns a function which returns -1 (i.e. a value which +// signals deletion in strings.Map) for runes in 'runes', and the rune otherwise. +func stripRunesFn(runes string) func(rune) rune { + return func(r rune) rune { + if strings.ContainsRune(runes, r) { + return -1 + } + return r + } +} + +// checkElement checks a given named element matches character and length restrictions. +// Returns true if the given element adheres to the given restrictions, false otherwise. +func checkElement(name, element, allowedRunes string, minRunes, maxRunes int) error { + numRunes := utf8.RuneCountInString(element) + if (numRunes < minRunes) || (maxRunes < numRunes) { + return newErrBadName("%s must be between %d and %d characters in length: %s", name, minRunes, maxRunes, element) + } else if len(strings.Map(stripRunesFn(allowedRunes), element)) != 0 { + return newErrBadName("%s can only contain the characters `%s`: %s", name, allowedRunes, element) + } + return nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/digest.go b/vendor/github.com/google/go-containerregistry/pkg/name/digest.go new file mode 100644 index 000000000..c049c1ef4 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/digest.go @@ -0,0 +1,94 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + // nolint: depguard + _ "crypto/sha256" // Recommended by go-digest. + "strings" + + "github.com/opencontainers/go-digest" +) + +const digestDelim = "@" + +// Digest stores a digest name in a structured form. +type Digest struct { + Repository + digest string + original string +} + +// Ensure Digest implements Reference +var _ Reference = (*Digest)(nil) + +// Context implements Reference. +func (d Digest) Context() Repository { + return d.Repository +} + +// Identifier implements Reference. +func (d Digest) Identifier() string { + return d.DigestStr() +} + +// DigestStr returns the digest component of the Digest. +func (d Digest) DigestStr() string { + return d.digest +} + +// Name returns the name from which the Digest was derived. +func (d Digest) Name() string { + return d.Repository.Name() + digestDelim + d.DigestStr() +} + +// String returns the original input string. +func (d Digest) String() string { + return d.original +} + +// NewDigest returns a new Digest representing the given name. +func NewDigest(name string, opts ...Option) (Digest, error) { + // Split on "@" + parts := strings.Split(name, digestDelim) + if len(parts) != 2 { + return Digest{}, newErrBadName("a digest must contain exactly one '@' separator (e.g. registry/repository@digest) saw: %s", name) + } + base := parts[0] + dig := parts[1] + prefix := digest.Canonical.String() + ":" + if !strings.HasPrefix(dig, prefix) { + return Digest{}, newErrBadName("unsupported digest algorithm: %s", dig) + } + hex := strings.TrimPrefix(dig, prefix) + if err := digest.Canonical.Validate(hex); err != nil { + return Digest{}, err + } + + tag, err := NewTag(base, opts...) + if err == nil { + base = tag.Repository.Name() + } + + repo, err := NewRepository(base, opts...) + if err != nil { + return Digest{}, err + } + return Digest{ + Repository: repo, + digest: dig, + original: name, + }, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/doc.go b/vendor/github.com/google/go-containerregistry/pkg/name/doc.go new file mode 100644 index 000000000..b294794dc --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/doc.go @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name defines structured types for representing image references. +// +// What's in a name? For image references, not nearly enough! +// +// Image references look a lot like URLs, but they differ in that they don't +// contain the scheme (http or https), they can end with a :tag or a @digest +// (the latter being validated), and they perform defaulting for missing +// components. +// +// Since image references don't contain the scheme, we do our best to infer +// if we use http or https from the given hostname. We allow http fallback for +// any host that looks like localhost (localhost, 127.0.0.1, ::1), ends in +// ".local", or is in the "private" address space per RFC 1918. For everything +// else, we assume https only. To override this heuristic, use the Insecure +// option. +// +// Image references with a digest signal to us that we should verify the content +// of the image matches the digest. E.g. when pulling a Digest reference, we'll +// calculate the sha256 of the manifest returned by the registry and error out +// if it doesn't match what we asked for. +// +// For defaulting, we interpret "ubuntu" as +// "index.docker.io/library/ubuntu:latest" because we add the missing repo +// "library", the missing registry "index.docker.io", and the missing tag +// "latest". To disable this defaulting, use the StrictValidation option. This +// is useful e.g. to only allow image references that explicitly set a tag or +// digest, so that you don't accidentally pull "latest". +package name diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/errors.go b/vendor/github.com/google/go-containerregistry/pkg/name/errors.go new file mode 100644 index 000000000..bf004ffcf --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/errors.go @@ -0,0 +1,48 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + "errors" + "fmt" +) + +// ErrBadName is an error for when a bad docker name is supplied. +type ErrBadName struct { + info string +} + +func (e *ErrBadName) Error() string { + return e.info +} + +// Is reports whether target is an error of type ErrBadName +func (e *ErrBadName) Is(target error) bool { + var berr *ErrBadName + return errors.As(target, &berr) +} + +// newErrBadName returns a ErrBadName which returns the given formatted string from Error(). +func newErrBadName(fmtStr string, args ...any) *ErrBadName { + return &ErrBadName{fmt.Sprintf(fmtStr, args...)} +} + +// IsErrBadName returns true if the given error is an ErrBadName. +// +// Deprecated: Use errors.Is. +func IsErrBadName(err error) bool { + var berr *ErrBadName + return errors.As(err, &berr) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/options.go b/vendor/github.com/google/go-containerregistry/pkg/name/options.go new file mode 100644 index 000000000..d14fedcda --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/options.go @@ -0,0 +1,83 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +const ( + // DefaultRegistry is the registry name that will be used if no registry + // provided and the default is not overridden. + DefaultRegistry = "index.docker.io" + defaultRegistryAlias = "docker.io" + + // DefaultTag is the tag name that will be used if no tag provided and the + // default is not overridden. + DefaultTag = "latest" +) + +type options struct { + strict bool // weak by default + insecure bool // secure by default + defaultRegistry string + defaultTag string +} + +func makeOptions(opts ...Option) options { + opt := options{ + defaultRegistry: DefaultRegistry, + defaultTag: DefaultTag, + } + for _, o := range opts { + o(&opt) + } + return opt +} + +// Option is a functional option for name parsing. +type Option func(*options) + +// StrictValidation is an Option that requires image references to be fully +// specified; i.e. no defaulting for registry (dockerhub), repo (library), +// or tag (latest). +func StrictValidation(opts *options) { + opts.strict = true +} + +// WeakValidation is an Option that sets defaults when parsing names, see +// StrictValidation. +func WeakValidation(opts *options) { + opts.strict = false +} + +// Insecure is an Option that allows image references to be fetched without TLS. +func Insecure(opts *options) { + opts.insecure = true +} + +// OptionFn is a function that returns an option. +type OptionFn func() Option + +// WithDefaultRegistry sets the default registry that will be used if one is not +// provided. +func WithDefaultRegistry(r string) Option { + return func(opts *options) { + opts.defaultRegistry = r + } +} + +// WithDefaultTag sets the default tag that will be used if one is not provided. +func WithDefaultTag(t string) Option { + return func(opts *options) { + opts.defaultTag = t + } +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/ref.go b/vendor/github.com/google/go-containerregistry/pkg/name/ref.go new file mode 100644 index 000000000..912ab3301 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/ref.go @@ -0,0 +1,75 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + "fmt" +) + +// Reference defines the interface that consumers use when they can +// take either a tag or a digest. +type Reference interface { + fmt.Stringer + + // Context accesses the Repository context of the reference. + Context() Repository + + // Identifier accesses the type-specific portion of the reference. + Identifier() string + + // Name is the fully-qualified reference name. + Name() string + + // Scope is the scope needed to access this reference. + Scope(string) string +} + +// ParseReference parses the string as a reference, either by tag or digest. +func ParseReference(s string, opts ...Option) (Reference, error) { + if t, err := NewTag(s, opts...); err == nil { + return t, nil + } + if d, err := NewDigest(s, opts...); err == nil { + return d, nil + } + return nil, newErrBadName("could not parse reference: " + s) +} + +type stringConst string + +// MustParseReference behaves like ParseReference, but panics instead of +// returning an error. It's intended for use in tests, or when a value is +// expected to be valid at code authoring time. +// +// To discourage its use in scenarios where the value is not known at code +// authoring time, it must be passed a string constant: +// +// const str = "valid/string" +// MustParseReference(str) +// MustParseReference("another/valid/string") +// MustParseReference(str + "/and/more") +// +// These will not compile: +// +// var str = "valid/string" +// MustParseReference(str) +// MustParseReference(strings.Join([]string{"valid", "string"}, "/")) +func MustParseReference(s stringConst, opts ...Option) Reference { + ref, err := ParseReference(string(s), opts...) + if err != nil { + panic(err) + } + return ref +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/registry.go b/vendor/github.com/google/go-containerregistry/pkg/name/registry.go new file mode 100644 index 000000000..5b0d01769 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/registry.go @@ -0,0 +1,142 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + "net" + "net/url" + "path" + "regexp" + "strings" +) + +// Detect more complex forms of local references. +var reLocal = regexp.MustCompile(`.*\.local(?:host)?(?::\d{1,5})?$`) + +// Detect the loopback IP (127.0.0.1) +var reLoopback = regexp.MustCompile(regexp.QuoteMeta("127.0.0.1")) + +// Detect the loopback IPV6 (::1) +var reipv6Loopback = regexp.MustCompile(regexp.QuoteMeta("::1")) + +// Registry stores a docker registry name in a structured form. +type Registry struct { + insecure bool + registry string +} + +// RegistryStr returns the registry component of the Registry. +func (r Registry) RegistryStr() string { + return r.registry +} + +// Name returns the name from which the Registry was derived. +func (r Registry) Name() string { + return r.RegistryStr() +} + +func (r Registry) String() string { + return r.Name() +} + +// Repo returns a Repository in the Registry with the given name. +func (r Registry) Repo(repo ...string) Repository { + return Repository{Registry: r, repository: path.Join(repo...)} +} + +// Scope returns the scope required to access the registry. +func (r Registry) Scope(string) string { + // The only resource under 'registry' is 'catalog'. http://goo.gl/N9cN9Z + return "registry:catalog:*" +} + +func (r Registry) isRFC1918() bool { + ipStr := strings.Split(r.Name(), ":")[0] + ip := net.ParseIP(ipStr) + if ip == nil { + return false + } + for _, cidr := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} { + _, block, _ := net.ParseCIDR(cidr) + if block.Contains(ip) { + return true + } + } + return false +} + +// Scheme returns https scheme for all the endpoints except localhost or when explicitly defined. +func (r Registry) Scheme() string { + if r.insecure { + return "http" + } + if r.isRFC1918() { + return "http" + } + if strings.HasPrefix(r.Name(), "localhost:") { + return "http" + } + if reLocal.MatchString(r.Name()) { + return "http" + } + if reLoopback.MatchString(r.Name()) { + return "http" + } + if reipv6Loopback.MatchString(r.Name()) { + return "http" + } + return "https" +} + +func checkRegistry(name string) error { + // Per RFC 3986, registries (authorities) are required to be prefixed with "//" + // url.Host == hostname[:port] == authority + if url, err := url.Parse("//" + name); err != nil || url.Host != name { + return newErrBadName("registries must be valid RFC 3986 URI authorities: %s", name) + } + return nil +} + +// NewRegistry returns a Registry based on the given name. +// Strict validation requires explicit, valid RFC 3986 URI authorities to be given. +func NewRegistry(name string, opts ...Option) (Registry, error) { + opt := makeOptions(opts...) + if opt.strict && len(name) == 0 { + return Registry{}, newErrBadName("strict validation requires the registry to be explicitly defined") + } + + if err := checkRegistry(name); err != nil { + return Registry{}, err + } + + if name == "" { + name = opt.defaultRegistry + } + // Rewrite "docker.io" to "index.docker.io". + // See: https://github.com/google/go-containerregistry/issues/68 + if name == defaultRegistryAlias { + name = DefaultRegistry + } + + return Registry{registry: name, insecure: opt.insecure}, nil +} + +// NewInsecureRegistry returns an Insecure Registry based on the given name. +// +// Deprecated: Use the Insecure Option with NewRegistry instead. +func NewInsecureRegistry(name string, opts ...Option) (Registry, error) { + opts = append(opts, Insecure) + return NewRegistry(name, opts...) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/repository.go b/vendor/github.com/google/go-containerregistry/pkg/name/repository.go new file mode 100644 index 000000000..9250e3625 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/repository.go @@ -0,0 +1,121 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + "fmt" + "strings" +) + +const ( + defaultNamespace = "library" + repositoryChars = "abcdefghijklmnopqrstuvwxyz0123456789_-./" + regRepoDelimiter = "/" +) + +// Repository stores a docker repository name in a structured form. +type Repository struct { + Registry + repository string +} + +// See https://docs.docker.com/docker-hub/official_repos +func hasImplicitNamespace(repo string, reg Registry) bool { + return !strings.ContainsRune(repo, '/') && reg.RegistryStr() == DefaultRegistry +} + +// RepositoryStr returns the repository component of the Repository. +func (r Repository) RepositoryStr() string { + if hasImplicitNamespace(r.repository, r.Registry) { + return fmt.Sprintf("%s/%s", defaultNamespace, r.repository) + } + return r.repository +} + +// Name returns the name from which the Repository was derived. +func (r Repository) Name() string { + regName := r.Registry.Name() + if regName != "" { + return regName + regRepoDelimiter + r.RepositoryStr() + } + // TODO: As far as I can tell, this is unreachable. + return r.RepositoryStr() +} + +func (r Repository) String() string { + return r.Name() +} + +// Scope returns the scope required to perform the given action on the registry. +// TODO(jonjohnsonjr): consider moving scopes to a separate package. +func (r Repository) Scope(action string) string { + return fmt.Sprintf("repository:%s:%s", r.RepositoryStr(), action) +} + +func checkRepository(repository string) error { + return checkElement("repository", repository, repositoryChars, 2, 255) +} + +// NewRepository returns a new Repository representing the given name, according to the given strictness. +func NewRepository(name string, opts ...Option) (Repository, error) { + opt := makeOptions(opts...) + if len(name) == 0 { + return Repository{}, newErrBadName("a repository name must be specified") + } + + var registry string + repo := name + parts := strings.SplitN(name, regRepoDelimiter, 2) + if len(parts) == 2 && (strings.ContainsRune(parts[0], '.') || strings.ContainsRune(parts[0], ':')) { + // The first part of the repository is treated as the registry domain + // iff it contains a '.' or ':' character, otherwise it is all repository + // and the domain defaults to Docker Hub. + registry = parts[0] + repo = parts[1] + } + + if err := checkRepository(repo); err != nil { + return Repository{}, err + } + + reg, err := NewRegistry(registry, opts...) + if err != nil { + return Repository{}, err + } + if hasImplicitNamespace(repo, reg) && opt.strict { + return Repository{}, newErrBadName("strict validation requires the full repository path (missing 'library')") + } + return Repository{reg, repo}, nil +} + +// Tag returns a Tag in this Repository. +func (r Repository) Tag(identifier string) Tag { + t := Tag{ + tag: identifier, + Repository: r, + } + t.original = t.Name() + return t +} + +// Digest returns a Digest in this Repository. +func (r Repository) Digest(identifier string) Digest { + d := Digest{ + digest: identifier, + Repository: r, + } + d.original = d.Name() + return d +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/name/tag.go b/vendor/github.com/google/go-containerregistry/pkg/name/tag.go new file mode 100644 index 000000000..66bd1bec3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/name/tag.go @@ -0,0 +1,108 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 name + +import ( + "strings" +) + +const ( + // TODO(dekkagaijin): use the docker/distribution regexes for validation. + tagChars = "abcdefghijklmnopqrstuvwxyz0123456789_-.ABCDEFGHIJKLMNOPQRSTUVWXYZ" + tagDelim = ":" +) + +// Tag stores a docker tag name in a structured form. +type Tag struct { + Repository + tag string + original string +} + +// Ensure Tag implements Reference +var _ Reference = (*Tag)(nil) + +// Context implements Reference. +func (t Tag) Context() Repository { + return t.Repository +} + +// Identifier implements Reference. +func (t Tag) Identifier() string { + return t.TagStr() +} + +// TagStr returns the tag component of the Tag. +func (t Tag) TagStr() string { + return t.tag +} + +// Name returns the name from which the Tag was derived. +func (t Tag) Name() string { + return t.Repository.Name() + tagDelim + t.TagStr() +} + +// String returns the original input string. +func (t Tag) String() string { + return t.original +} + +// Scope returns the scope required to perform the given action on the tag. +func (t Tag) Scope(action string) string { + return t.Repository.Scope(action) +} + +func checkTag(name string) error { + return checkElement("tag", name, tagChars, 1, 128) +} + +// NewTag returns a new Tag representing the given name, according to the given strictness. +func NewTag(name string, opts ...Option) (Tag, error) { + opt := makeOptions(opts...) + base := name + tag := "" + + // Split on ":" + parts := strings.Split(name, tagDelim) + // Verify that we aren't confusing a tag for a hostname w/ port for the purposes of weak validation. + if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], regRepoDelimiter) { + base = strings.Join(parts[:len(parts)-1], tagDelim) + tag = parts[len(parts)-1] + } + + // We don't require a tag, but if we get one check it's valid, + // even when not being strict. + // If we are being strict, we want to validate the tag regardless in case + // it's empty. + if tag != "" || opt.strict { + if err := checkTag(tag); err != nil { + return Tag{}, err + } + } + + if tag == "" { + tag = opt.defaultTag + } + + repo, err := NewRepository(base, opts...) + if err != nil { + return Tag{}, err + } + return Tag{ + Repository: repo, + tag: tag, + original: name, + }, nil +} diff --git a/vendor/github.com/opencontainers/go-digest/.mailmap b/vendor/github.com/opencontainers/go-digest/.mailmap new file mode 100644 index 000000000..eaf8b2f9e --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.mailmap @@ -0,0 +1,4 @@ +Aaron Lehmann +Derek McGowan +Stephen J Day +Haibing Zhou diff --git a/vendor/github.com/opencontainers/go-digest/.pullapprove.yml b/vendor/github.com/opencontainers/go-digest/.pullapprove.yml new file mode 100644 index 000000000..b6165f83c --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.pullapprove.yml @@ -0,0 +1,28 @@ +version: 2 + +requirements: + signed_off_by: + required: true + +always_pending: + title_regex: '^WIP' + explanation: 'Work in progress...' + +group_defaults: + required: 2 + approve_by_comment: + enabled: true + approve_regex: '^LGTM' + reject_regex: '^Rejected' + reset_on_push: + enabled: true + author_approval: + ignored: true + conditions: + branches: + - master + +groups: + go-digest: + teams: + - go-digest-maintainers diff --git a/vendor/github.com/opencontainers/go-digest/.travis.yml b/vendor/github.com/opencontainers/go-digest/.travis.yml new file mode 100644 index 000000000..5775f885c --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/.travis.yml @@ -0,0 +1,5 @@ +language: go +go: + - 1.12.x + - 1.13.x + - master diff --git a/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md b/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md new file mode 100644 index 000000000..e4d962ac1 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to Docker open source projects + +Want to hack on this project? Awesome! Here are instructions to get you started. + +This project is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +For an in-depth description of our contribution process, visit the +contributors guide: [Understand how to contribute](https://docs.docker.com/opensource/workflow/make-a-contribution/) + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE b/vendor/github.com/opencontainers/go-digest/LICENSE new file mode 100644 index 000000000..3ac8ab648 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2019, 2020 OCI Contributors + Copyright 2016 Docker, Inc. + + 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 + + https://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. diff --git a/vendor/github.com/opencontainers/go-digest/LICENSE.docs b/vendor/github.com/opencontainers/go-digest/LICENSE.docs new file mode 100644 index 000000000..e26cd4fc8 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/vendor/github.com/opencontainers/go-digest/MAINTAINERS b/vendor/github.com/opencontainers/go-digest/MAINTAINERS new file mode 100644 index 000000000..843b1b206 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/MAINTAINERS @@ -0,0 +1,5 @@ +Derek McGowan (@dmcgowan) +Stephen Day (@stevvooe) +Vincent Batts (@vbatts) +Akihiro Suda (@AkihiroSuda) +Sebastiaan van Stijn (@thaJeztah) diff --git a/vendor/github.com/opencontainers/go-digest/README.md b/vendor/github.com/opencontainers/go-digest/README.md new file mode 100644 index 000000000..a11287207 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/README.md @@ -0,0 +1,96 @@ +# go-digest + +[![GoDoc](https://godoc.org/github.com/opencontainers/go-digest?status.svg)](https://godoc.org/github.com/opencontainers/go-digest) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/go-digest)](https://goreportcard.com/report/github.com/opencontainers/go-digest) [![Build Status](https://travis-ci.org/opencontainers/go-digest.svg?branch=master)](https://travis-ci.org/opencontainers/go-digest) + +Common digest package used across the container ecosystem. + +Please see the [godoc](https://godoc.org/github.com/opencontainers/go-digest) for more information. + +# What is a digest? + +A digest is just a [hash](https://en.wikipedia.org/wiki/Hash_function). + +The most common use case for a digest is to create a content identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage) systems: + +```go +id := digest.FromBytes([]byte("my content")) +``` + +In the example above, the id can be used to uniquely identify the byte slice "my content". +This allows two disparate applications to agree on a verifiable identifier without having to trust one another. + +An identifying digest can be verified, as follows: + +```go +if id != digest.FromBytes([]byte("my content")) { + return errors.New("the content has changed!") +} +``` + +A `Verifier` type can be used to handle cases where an `io.Reader` makes more sense: + +```go +rd := getContent() +verifier := id.Verifier() +io.Copy(verifier, rd) + +if !verifier.Verified() { + return errors.New("the content has changed!") +} +``` + +Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this can power a rich, safe, content distribution system. + +# Usage + +While the [godoc](https://godoc.org/github.com/opencontainers/go-digest) is considered the best resource, a few important items need to be called out when using this package. + +1. Make sure to import the hash implementations into your application or the package will panic. + You should have something like the following in the main (or other entrypoint) of your application: + + ```go + import ( + _ "crypto/sha256" + _ "crypto/sha512" + ) + ``` + This may seem inconvenient but it allows you replace the hash + implementations with others, such as https://github.com/stevvooe/resumable. + +2. Even though `digest.Digest` may be assemblable as a string, _always_ verify your input with `digest.Parse` or use `Digest.Validate` when accepting untrusted input. + While there are measures to avoid common problems, this will ensure you have valid digests in the rest of your application. + +3. While alternative encodings of hash values (digests) are possible (for example, base64), this package deals exclusively with hex-encoded digests. + +# Stability + +The Go API, at this stage, is considered stable, unless otherwise noted. + +As always, before using a package export, read the [godoc](https://godoc.org/github.com/opencontainers/go-digest). + +# Contributing + +This package is considered fairly complete. +It has been in production in thousands (millions?) of deployments and is fairly battle-hardened. +New additions will be met with skepticism. +If you think there is a missing feature, please file a bug clearly describing the problem and the alternatives you tried before submitting a PR. + +## Code of Conduct + +Participation in the OpenContainers community is governed by [OpenContainer's Code of Conduct][code-of-conduct]. + +## Security + +If you find an issue, please follow the [security][security] protocol to report it. + +# Copyright and license + +Copyright © 2019, 2020 OCI Contributors +Copyright © 2016 Docker, Inc. +All rights reserved, except as follows. +Code is released under the [Apache 2.0 license](LICENSE). +This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs). +You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/. + +[security]: https://github.com/opencontainers/org/blob/master/security +[code-of-conduct]: https://github.com/opencontainers/org/blob/master/CODE_OF_CONDUCT.md diff --git a/vendor/github.com/opencontainers/go-digest/algorithm.go b/vendor/github.com/opencontainers/go-digest/algorithm.go new file mode 100644 index 000000000..490951dc3 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/algorithm.go @@ -0,0 +1,193 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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 digest + +import ( + "crypto" + "fmt" + "hash" + "io" + "regexp" +) + +// Algorithm identifies and implementation of a digester by an identifier. +// Note the that this defines both the hash algorithm used and the string +// encoding. +type Algorithm string + +// supported digest types +const ( + SHA256 Algorithm = "sha256" // sha256 with hex encoding (lower case only) + SHA384 Algorithm = "sha384" // sha384 with hex encoding (lower case only) + SHA512 Algorithm = "sha512" // sha512 with hex encoding (lower case only) + + // Canonical is the primary digest algorithm used with the distribution + // project. Other digests may be used but this one is the primary storage + // digest. + Canonical = SHA256 +) + +var ( + // TODO(stevvooe): Follow the pattern of the standard crypto package for + // registration of digests. Effectively, we are a registerable set and + // common symbol access. + + // algorithms maps values to hash.Hash implementations. Other algorithms + // may be available but they cannot be calculated by the digest package. + algorithms = map[Algorithm]crypto.Hash{ + SHA256: crypto.SHA256, + SHA384: crypto.SHA384, + SHA512: crypto.SHA512, + } + + // anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests. + // Note that /A-F/ disallowed. + anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{ + SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`), + SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`), + SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`), + } +) + +// Available returns true if the digest type is available for use. If this +// returns false, Digester and Hash will return nil. +func (a Algorithm) Available() bool { + h, ok := algorithms[a] + if !ok { + return false + } + + // check availability of the hash, as well + return h.Available() +} + +func (a Algorithm) String() string { + return string(a) +} + +// Size returns number of bytes returned by the hash. +func (a Algorithm) Size() int { + h, ok := algorithms[a] + if !ok { + return 0 + } + return h.Size() +} + +// Set implemented to allow use of Algorithm as a command line flag. +func (a *Algorithm) Set(value string) error { + if value == "" { + *a = Canonical + } else { + // just do a type conversion, support is queried with Available. + *a = Algorithm(value) + } + + if !a.Available() { + return ErrDigestUnsupported + } + + return nil +} + +// Digester returns a new digester for the specified algorithm. If the algorithm +// does not have a digester implementation, nil will be returned. This can be +// checked by calling Available before calling Digester. +func (a Algorithm) Digester() Digester { + return &digester{ + alg: a, + hash: a.Hash(), + } +} + +// Hash returns a new hash as used by the algorithm. If not available, the +// method will panic. Check Algorithm.Available() before calling. +func (a Algorithm) Hash() hash.Hash { + if !a.Available() { + // Empty algorithm string is invalid + if a == "" { + panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()")) + } + + // NOTE(stevvooe): A missing hash is usually a programming error that + // must be resolved at compile time. We don't import in the digest + // package to allow users to choose their hash implementation (such as + // when using stevvooe/resumable or a hardware accelerated package). + // + // Applications that may want to resolve the hash at runtime should + // call Algorithm.Available before call Algorithm.Hash(). + panic(fmt.Sprintf("%v not available (make sure it is imported)", a)) + } + + return algorithms[a].New() +} + +// Encode encodes the raw bytes of a digest, typically from a hash.Hash, into +// the encoded portion of the digest. +func (a Algorithm) Encode(d []byte) string { + // TODO(stevvooe): Currently, all algorithms use a hex encoding. When we + // add support for back registration, we can modify this accordingly. + return fmt.Sprintf("%x", d) +} + +// FromReader returns the digest of the reader using the algorithm. +func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { + digester := a.Digester() + + if _, err := io.Copy(digester.Hash(), rd); err != nil { + return "", err + } + + return digester.Digest(), nil +} + +// FromBytes digests the input and returns a Digest. +func (a Algorithm) FromBytes(p []byte) Digest { + digester := a.Digester() + + if _, err := digester.Hash().Write(p); err != nil { + // Writes to a Hash should never fail. None of the existing + // hash implementations in the stdlib or hashes vendored + // here can return errors from Write. Having a panic in this + // condition instead of having FromBytes return an error value + // avoids unnecessary error handling paths in all callers. + panic("write to hash function returned error: " + err.Error()) + } + + return digester.Digest() +} + +// FromString digests the string input and returns a Digest. +func (a Algorithm) FromString(s string) Digest { + return a.FromBytes([]byte(s)) +} + +// Validate validates the encoded portion string +func (a Algorithm) Validate(encoded string) error { + r, ok := anchoredEncodedRegexps[a] + if !ok { + return ErrDigestUnsupported + } + // Digests much always be hex-encoded, ensuring that their hex portion will + // always be size*2 + if a.Size()*2 != len(encoded) { + return ErrDigestInvalidLength + } + if r.MatchString(encoded) { + return nil + } + return ErrDigestInvalidFormat +} diff --git a/vendor/github.com/opencontainers/go-digest/digest.go b/vendor/github.com/opencontainers/go-digest/digest.go new file mode 100644 index 000000000..518b5e715 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/digest.go @@ -0,0 +1,157 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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 digest + +import ( + "fmt" + "hash" + "io" + "regexp" + "strings" +) + +// Digest allows simple protection of hex formatted digest strings, prefixed +// by their algorithm. Strings of type Digest have some guarantee of being in +// the correct format and it provides quick access to the components of a +// digest string. +// +// The following is an example of the contents of Digest types: +// +// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc +// +// This allows to abstract the digest behind this type and work only in those +// terms. +type Digest string + +// NewDigest returns a Digest from alg and a hash.Hash object. +func NewDigest(alg Algorithm, h hash.Hash) Digest { + return NewDigestFromBytes(alg, h.Sum(nil)) +} + +// NewDigestFromBytes returns a new digest from the byte contents of p. +// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) +// functions. This is also useful for rebuilding digests from binary +// serializations. +func NewDigestFromBytes(alg Algorithm, p []byte) Digest { + return NewDigestFromEncoded(alg, alg.Encode(p)) +} + +// NewDigestFromHex is deprecated. Please use NewDigestFromEncoded. +func NewDigestFromHex(alg, hex string) Digest { + return NewDigestFromEncoded(Algorithm(alg), hex) +} + +// NewDigestFromEncoded returns a Digest from alg and the encoded digest. +func NewDigestFromEncoded(alg Algorithm, encoded string) Digest { + return Digest(fmt.Sprintf("%s:%s", alg, encoded)) +} + +// DigestRegexp matches valid digest types. +var DigestRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+`) + +// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. +var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) + +var ( + // ErrDigestInvalidFormat returned when digest format invalid. + ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") + + // ErrDigestInvalidLength returned when digest has invalid length. + ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") + + // ErrDigestUnsupported returned when the digest algorithm is unsupported. + ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") +) + +// Parse parses s and returns the validated digest object. An error will +// be returned if the format is invalid. +func Parse(s string) (Digest, error) { + d := Digest(s) + return d, d.Validate() +} + +// FromReader consumes the content of rd until io.EOF, returning canonical digest. +func FromReader(rd io.Reader) (Digest, error) { + return Canonical.FromReader(rd) +} + +// FromBytes digests the input and returns a Digest. +func FromBytes(p []byte) Digest { + return Canonical.FromBytes(p) +} + +// FromString digests the input and returns a Digest. +func FromString(s string) Digest { + return Canonical.FromString(s) +} + +// Validate checks that the contents of d is a valid digest, returning an +// error if not. +func (d Digest) Validate() error { + s := string(d) + i := strings.Index(s, ":") + if i <= 0 || i+1 == len(s) { + return ErrDigestInvalidFormat + } + algorithm, encoded := Algorithm(s[:i]), s[i+1:] + if !algorithm.Available() { + if !DigestRegexpAnchored.MatchString(s) { + return ErrDigestInvalidFormat + } + return ErrDigestUnsupported + } + return algorithm.Validate(encoded) +} + +// Algorithm returns the algorithm portion of the digest. This will panic if +// the underlying digest is not in a valid format. +func (d Digest) Algorithm() Algorithm { + return Algorithm(d[:d.sepIndex()]) +} + +// Verifier returns a writer object that can be used to verify a stream of +// content against the digest. If the digest is invalid, the method will panic. +func (d Digest) Verifier() Verifier { + return hashVerifier{ + hash: d.Algorithm().Hash(), + digest: d, + } +} + +// Encoded returns the encoded portion of the digest. This will panic if the +// underlying digest is not in a valid format. +func (d Digest) Encoded() string { + return string(d[d.sepIndex()+1:]) +} + +// Hex is deprecated. Please use Digest.Encoded. +func (d Digest) Hex() string { + return d.Encoded() +} + +func (d Digest) String() string { + return string(d) +} + +func (d Digest) sepIndex() int { + i := strings.Index(string(d), ":") + + if i < 0 { + panic(fmt.Sprintf("no ':' separator in digest %q", d)) + } + + return i +} diff --git a/vendor/github.com/opencontainers/go-digest/digester.go b/vendor/github.com/opencontainers/go-digest/digester.go new file mode 100644 index 000000000..ede907757 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/digester.go @@ -0,0 +1,40 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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 digest + +import "hash" + +// Digester calculates the digest of written data. Writes should go directly +// to the return value of Hash, while calling Digest will return the current +// value of the digest. +type Digester interface { + Hash() hash.Hash // provides direct access to underlying hash instance. + Digest() Digest +} + +// digester provides a simple digester definition that embeds a hasher. +type digester struct { + alg Algorithm + hash hash.Hash +} + +func (d *digester) Hash() hash.Hash { + return d.hash +} + +func (d *digester) Digest() Digest { + return NewDigest(d.alg, d.hash) +} diff --git a/vendor/github.com/opencontainers/go-digest/doc.go b/vendor/github.com/opencontainers/go-digest/doc.go new file mode 100644 index 000000000..83d3a936c --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/doc.go @@ -0,0 +1,62 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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 digest provides a generalized type to opaquely represent message +// digests and their operations within the registry. The Digest type is +// designed to serve as a flexible identifier in a content-addressable system. +// More importantly, it provides tools and wrappers to work with +// hash.Hash-based digests with little effort. +// +// Basics +// +// The format of a digest is simply a string with two parts, dubbed the +// "algorithm" and the "digest", separated by a colon: +// +// : +// +// An example of a sha256 digest representation follows: +// +// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc +// +// The "algorithm" portion defines both the hashing algorithm used to calculate +// the digest and the encoding of the resulting digest, which defaults to "hex" +// if not otherwise specified. Currently, all supported algorithms have their +// digests encoded in hex strings. +// +// In the example above, the string "sha256" is the algorithm and the hex bytes +// are the "digest". +// +// Because the Digest type is simply a string, once a valid Digest is +// obtained, comparisons are cheap, quick and simple to express with the +// standard equality operator. +// +// Verification +// +// The main benefit of using the Digest type is simple verification against a +// given digest. The Verifier interface, modeled after the stdlib hash.Hash +// interface, provides a common write sink for digest verification. After +// writing is complete, calling the Verifier.Verified method will indicate +// whether or not the stream of bytes matches the target digest. +// +// Missing Features +// +// In addition to the above, we intend to add the following features to this +// package: +// +// 1. A Digester type that supports write sink digest calculation. +// +// 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry. +// +package digest diff --git a/vendor/github.com/opencontainers/go-digest/verifiers.go b/vendor/github.com/opencontainers/go-digest/verifiers.go new file mode 100644 index 000000000..afef506f4 --- /dev/null +++ b/vendor/github.com/opencontainers/go-digest/verifiers.go @@ -0,0 +1,46 @@ +// Copyright 2019, 2020 OCI Contributors +// Copyright 2017 Docker, Inc. +// +// 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 +// +// https://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 digest + +import ( + "hash" + "io" +) + +// Verifier presents a general verification interface to be used with message +// digests and other byte stream verifications. Users instantiate a Verifier +// from one of the various methods, write the data under test to it then check +// the result with the Verified method. +type Verifier interface { + io.Writer + + // Verified will return true if the content written to Verifier matches + // the digest. + Verified() bool +} + +type hashVerifier struct { + digest Digest + hash hash.Hash +} + +func (hv hashVerifier) Write(p []byte) (n int, err error) { + return hv.hash.Write(p) +} + +func (hv hashVerifier) Verified() bool { + return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash) +} diff --git a/vendor/kmodules.xyz/client-go/client/apiutil/cachable.go b/vendor/kmodules.xyz/client-go/client/apiutil/cachable.go new file mode 100644 index 000000000..7558630f9 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/apiutil/cachable.go @@ -0,0 +1,103 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 apiutil + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" +) + +type Cachable interface { + GVK(gvk schema.GroupVersionKind) (bool, error) + GVR(gvr schema.GroupVersionResource) (bool, error) +} + +type cachable struct { + resources map[string][]metav1.APIResource +} + +var _ Cachable = &cachable{} + +func NewCachable(cl discovery.DiscoveryInterface) (Cachable, error) { + _, list, err := cl.ServerGroupsAndResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + return nil, err + } + m := map[string][]metav1.APIResource{} + for _, rl := range list { + m[rl.GroupVersion] = rl.APIResources + } + return &cachable{resources: m}, nil +} + +func (c *cachable) GVK(gvk schema.GroupVersionKind) (bool, error) { + rl, ok := c.resources[gvk.GroupVersion().String()] + if !ok { + return false, &meta.NoKindMatchError{ + GroupKind: gvk.GroupKind(), + SearchedVersions: []string{gvk.Version}, + } + } + for _, r := range rl { + if r.Kind != gvk.Kind { + continue + } + var canList, canWatch bool + for _, verb := range r.Verbs { + if verb == "list" { + canList = true + } + if verb == "watch" { + canWatch = true + } + } + return canList && canWatch, nil + } + return false, &meta.NoKindMatchError{ + GroupKind: gvk.GroupKind(), + SearchedVersions: []string{gvk.Version}, + } +} + +func (c *cachable) GVR(gvr schema.GroupVersionResource) (bool, error) { + rl, ok := c.resources[gvr.GroupVersion().String()] + if !ok { + return false, &meta.NoResourceMatchError{ + PartialResource: gvr, + } + } + for _, r := range rl { + if r.Name != gvr.Resource { + continue + } + var canList, canWatch bool + for _, verb := range r.Verbs { + if verb == "list" { + canList = true + } + if verb == "watch" { + canWatch = true + } + } + return canList && canWatch, nil + } + return false, &meta.NoResourceMatchError{ + PartialResource: gvr, + } +} diff --git a/vendor/kmodules.xyz/client-go/client/apiutil/dynamic.go b/vendor/kmodules.xyz/client-go/client/apiutil/dynamic.go new file mode 100644 index 000000000..3235c9d60 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/apiutil/dynamic.go @@ -0,0 +1,219 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 apiutil + +import ( + "sync" + "sync/atomic" + + "golang.org/x/time/rate" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/rest" +) + +// Adapted from https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/client/apiutil/dynamicrestmapper.go + +// dynamicCachable is a Cachable that dynamically discovers resource +// types at runtime. +type dynamicCachable struct { + mu sync.RWMutex // protects the following fields + staticCachable Cachable + limiter *rate.Limiter + newCachable func() (Cachable, error) + + lazy bool + // Used for lazy init. + inited uint32 + initMtx sync.Mutex +} + +// DynamicCachableOption is a functional option on the dynamicCachable +type DynamicCachableOption func(*dynamicCachable) error + +// WithLimiter sets the Cachable's underlying limiter to lim. +func WithLimiter(lim *rate.Limiter) DynamicCachableOption { + return func(drm *dynamicCachable) error { + drm.limiter = lim + return nil + } +} + +// WithLazyDiscovery prevents the Cachable from discovering REST mappings +// until an API call is made. +var WithLazyDiscovery DynamicCachableOption = func(drm *dynamicCachable) error { + drm.lazy = true + return nil +} + +// WithCustomCachable supports setting a custom Cachable refresher instead of +// the default method, which uses a discovery client. +// +// This exists mainly for testing, but can be useful if you need tighter control +// over how discovery is performed, which discovery endpoints are queried, etc. +func WithCustomCachable(newCachable func() (Cachable, error)) DynamicCachableOption { + return func(drm *dynamicCachable) error { + drm.newCachable = newCachable + return nil + } +} + +// NewDynamicCachable returns a dynamic Cachable for cfg. The dynamic +// Cachable dynamically discovers resource types at runtime. opts +// configure the Cachable. +func NewDynamicCachable(cfg *rest.Config, opts ...DynamicCachableOption) (Cachable, error) { + client, err := discovery.NewDiscoveryClientForConfig(cfg) + if err != nil { + return nil, err + } + drm := &dynamicCachable{ + limiter: rate.NewLimiter(rate.Limit(defaultRefillRate), defaultLimitSize), + newCachable: func() (Cachable, error) { + return NewCachable(client) + }, + } + for _, opt := range opts { + if err = opt(drm); err != nil { + return nil, err + } + } + if !drm.lazy { + if err := drm.setStaticCachable(); err != nil { + return nil, err + } + } + return drm, nil +} + +var ( + // defaultRefilRate is the default rate at which potential calls are + // added back to the "bucket" of allowed calls. + defaultRefillRate = 5 + // defaultLimitSize is the default starting/max number of potential calls + // per second. Once a call is used, it's added back to the bucket at a rate + // of defaultRefillRate per second. + defaultLimitSize = 5 +) + +// setStaticCachable sets drm's staticCachable by querying its client, regardless +// of reload backoff. +func (drm *dynamicCachable) setStaticCachable() error { + newCachable, err := drm.newCachable() + if err != nil { + return err + } + drm.staticCachable = newCachable + return nil +} + +// init initializes drm only once if drm is lazy. +func (drm *dynamicCachable) init() (err error) { + // skip init if drm is not lazy or has initialized + if !drm.lazy || atomic.LoadUint32(&drm.inited) != 0 { + return nil + } + + drm.initMtx.Lock() + defer drm.initMtx.Unlock() + if drm.inited == 0 { + if err = drm.setStaticCachable(); err == nil { + atomic.StoreUint32(&drm.inited, 1) + } + } + return err +} + +// checkAndReload attempts to call the given callback, which is assumed to be dependent +// on the data in the restmapper. +// +// If the callback returns an error matching meta.IsNoMatchErr, it will attempt to reload +// the Cachable's data and re-call the callback once that's occurred. +// If the callback returns any other error, the function will return immediately regardless. +// +// It will take care of ensuring that reloads are rate-limited and that extraneous calls +// aren't made. If a reload would exceed the limiters rate, it returns the error return by +// the callback. +// It's thread-safe, and worries about thread-safety for the callback (so the callback does +// not need to attempt to lock the restmapper). +func (drm *dynamicCachable) checkAndReload(checkNeedsReload func() error) error { + // first, check the common path -- data is fresh enough + // (use an IIFE for the lock's defer) + err := func() error { + drm.mu.RLock() + defer drm.mu.RUnlock() + + return checkNeedsReload() + }() + + needsReload := meta.IsNoMatchError(err) + if !needsReload { + return err + } + + // if the data wasn't fresh, we'll need to try and update it, so grab the lock... + drm.mu.Lock() + defer drm.mu.Unlock() + + // ... and double-check that we didn't reload in the meantime + err = checkNeedsReload() + needsReload = meta.IsNoMatchError(err) + if !needsReload { + return err + } + + // we're still stale, so grab a rate-limit token if we can... + if !drm.limiter.Allow() { + // return error from static mapper here, we have refreshed often enough (exceeding rate of provided limiter) + // so that client's can handle this the same way as a "normal" NoResourceMatchError / NoKindMatchError + return err + } + + // ...reload... + if err := drm.setStaticCachable(); err != nil { + return err + } + + // ...and return the results of the closure regardless + return checkNeedsReload() +} + +func (drm *dynamicCachable) GVK(gvk schema.GroupVersionKind) (bool, error) { + if err := drm.init(); err != nil { + return false, err + } + var canCache bool + err := drm.checkAndReload(func() error { + var err error + canCache, err = drm.staticCachable.GVK(gvk) + return err + }) + return canCache, err +} + +func (drm *dynamicCachable) GVR(gvr schema.GroupVersionResource) (bool, error) { + if err := drm.init(); err != nil { + return false, err + } + var canCache bool + err := drm.checkAndReload(func() error { + var err error + canCache, err = drm.staticCachable.GVR(gvr) + return err + }) + return canCache, err +} diff --git a/vendor/kmodules.xyz/client-go/client/apiutil/pods.go b/vendor/kmodules.xyz/client-go/client/apiutil/pods.go new file mode 100644 index 000000000..b4de6c5c9 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/apiutil/pods.go @@ -0,0 +1,237 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 apiutil + +import ( + "context" + "strings" + + kmapi "kmodules.xyz/client-go/api/v1" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func CollectImageInfo(kc client.Client, pod *core.Pod, images map[string]kmapi.ImageInfo, fullLineage bool) (map[string]kmapi.ImageInfo, error) { + var lineage []kmapi.ObjectInfo + + var err error + if fullLineage { + lineage, err = DetectLineage(context.TODO(), kc, pod) + if err != nil { + return images, err + } + } else { + lineage = []kmapi.ObjectInfo{ + { + Resource: kmapi.ResourceID{ + Group: "", + Version: "v1", + Name: "pods", + Kind: "Pod", + Scope: kmapi.NamespaceScoped, + }, + Ref: kmapi.ObjectReference{ + Namespace: pod.Namespace, + Name: pod.Name, + }, + }, + } + } + + refs := map[string][]string{} + for _, c := range pod.Spec.Containers { + si, sid := findContainerStatus(c.Name, pod.Status.ContainerStatuses) + ref, err := GetImageRef(c.Image, si, sid) + if err != nil { + return images, err + } + refs[ref] = append(refs[ref], c.Name) + } + for _, c := range pod.Spec.InitContainers { + si, sid := findContainerStatus(c.Name, pod.Status.InitContainerStatuses) + ref, err := GetImageRef(c.Image, si, sid) + if err != nil { + return images, err + } + refs[ref] = append(refs[ref], c.Name) + } + for _, c := range pod.Spec.EphemeralContainers { + si, sid := findContainerStatus(c.Name, pod.Status.EphemeralContainerStatuses) + ref, err := GetImageRef(c.Image, si, sid) + if err != nil { + return images, err + } + refs[ref] = append(refs[ref], c.Name) + } + + for ref, containers := range refs { + iu, ok := images[ref] + if !ok { + iu = kmapi.ImageInfo{ + Image: ref, + Lineages: nil, + PullCredentials: &kmapi.PullCredentials{ + Namespace: pod.Namespace, + SecretRefs: pod.Spec.ImagePullSecrets, + ServiceAccountName: pod.Spec.ServiceAccountName, + }, + } + } + iu.Lineages = append(iu.Lineages, kmapi.Lineage{ + Chain: lineage, + Containers: containers, + }) + images[ref] = iu + } + + return images, nil +} + +func CollectPullCredentials(pod *core.Pod, refs map[string]kmapi.PullCredentials) (map[string]kmapi.PullCredentials, error) { + for _, c := range pod.Spec.Containers { + si, sid := findContainerStatus(c.Name, pod.Status.ContainerStatuses) + ref, err := GetImageRef(c.Image, si, sid) + if err != nil { + return refs, err + } + refs[ref] = kmapi.PullCredentials{ + Namespace: pod.Namespace, + SecretRefs: pod.Spec.ImagePullSecrets, + ServiceAccountName: pod.Spec.ServiceAccountName, + } + } + for _, c := range pod.Spec.InitContainers { + si, sid := findContainerStatus(c.Name, pod.Status.InitContainerStatuses) + ref, err := GetImageRef(c.Image, si, sid) + if err != nil { + return refs, err + } + refs[ref] = kmapi.PullCredentials{ + Namespace: pod.Namespace, + SecretRefs: pod.Spec.ImagePullSecrets, + ServiceAccountName: pod.Spec.ServiceAccountName, + } + } + for _, c := range pod.Spec.EphemeralContainers { + si, sid := findContainerStatus(c.Name, pod.Status.EphemeralContainerStatuses) + ref, err := GetImageRef(c.Image, si, sid) + if err != nil { + return refs, err + } + refs[ref] = kmapi.PullCredentials{ + Namespace: pod.Namespace, + SecretRefs: pod.Spec.ImagePullSecrets, + ServiceAccountName: pod.Spec.ServiceAccountName, + } + } + + return refs, nil +} + +func GetImageRef(containerImage, statusImage, statusImageID string) (string, error) { + var img string + + if strings.ContainsRune(containerImage, '@') { + img = containerImage + } else if strings.ContainsRune(statusImage, '@') { + img = statusImage + } else { + // take the hash from status.ImageID and add to c.Image + if strings.Contains(statusImageID, "://") { + statusImageID = statusImageID[strings.Index(statusImageID, "://")+3:] // remove docker-pullable:// + } + + // Now check imageID is using same repo as the contianerImage + // This will not be same for images loaded into a KIND cluster + + isSameContext := func(img1, img2 string) bool { + ref1, err := name.ParseReference(img1) + if err != nil { + return false + } + ref2, err := name.ParseReference(img2) + if err != nil { + return false + } + return ref1.Context().String() == ref2.Context().String() + } + + _, digest, ok := strings.Cut(statusImageID, "@") + if isSameContext(containerImage, statusImageID) && ok { + img = containerImage + "@" + digest + } else { + img = containerImage + // return "", fmt.Errorf("missing digest in pod %s container %s imageID %s", pod, status.Name, status.ImageID) + } + } + + ref, err := name.ParseReference(img) + if err != nil { + return "", errors.Wrapf(err, "ref=%s", img) + } + return ref.Name(), nil +} + +func findContainerStatus(name string, statuses []core.ContainerStatus) (string, string) { + for _, s := range statuses { + if s.Name == name { + return s.Image, s.ImageID + } + } + return "", "" +} + +func DetectLineage(ctx context.Context, kc client.Client, obj client.Object) ([]kmapi.ObjectInfo, error) { + return findLineage(ctx, kc, obj, nil) +} + +func findLineage(ctx context.Context, kc client.Client, obj client.Object, result []kmapi.ObjectInfo) ([]kmapi.ObjectInfo, error) { + ref := metav1.GetControllerOfNoCopy(obj) + if ref != nil { + var owner unstructured.Unstructured + owner.SetAPIVersion(ref.APIVersion) + owner.SetKind(ref.Kind) + if err := kc.Get(ctx, client.ObjectKey{Namespace: obj.GetNamespace(), Name: ref.Name}, &owner); client.IgnoreNotFound(err) != nil { + return result, err + } else if err == nil { // ignore not found error, owner might be already deleted + var err error + result, err = findLineage(ctx, kc, &owner, result) + if err != nil { + return result, err + } + } + } + + gvk := obj.GetObjectKind().GroupVersionKind() + mapping, err := kc.RESTMapper().RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + return nil, err + } + result = append(result, kmapi.ObjectInfo{ + Resource: *kmapi.NewResourceID(mapping), + Ref: kmapi.ObjectReference{ + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + }, + }) + return result, nil +} diff --git a/vendor/kmodules.xyz/client-go/client/client.go b/vendor/kmodules.xyz/client-go/client/client.go new file mode 100644 index 000000000..e24b7b4c3 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/client.go @@ -0,0 +1,189 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 client + +import ( + "context" + "reflect" + "strings" + + "github.com/pkg/errors" + kerr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +func NewUncachedClient(cfg *rest.Config, funcs ...func(*runtime.Scheme) error) (client.Client, error) { + hc, err := rest.HTTPClientFor(cfg) + if err != nil { + return nil, err + } + mapper, err := apiutil.NewDynamicRESTMapper(cfg, hc) + if err != nil { + return nil, err + } + + builder := runtime.NewSchemeBuilder(funcs...) + builder.Register(clientgoscheme.AddToScheme) + scheme := runtime.NewScheme() + err = builder.AddToScheme(scheme) + if err != nil { + return nil, err + } + + return client.New(cfg, client.Options{ + Scheme: scheme, + Mapper: mapper, + //Opts: client.WarningHandlerOptions{ + // SuppressWarnings: false, + // AllowDuplicateLogs: false, + //}, + }) +} + +type ( + TransformFunc func(obj client.Object, createOp bool) client.Object + TransformStatusFunc func(obj client.Object) client.Object +) + +func CreateOrPatch(ctx context.Context, c client.Client, obj client.Object, transform TransformFunc, opts ...client.PatchOption) (kutil.VerbType, error) { + gvk, err := apiutil.GVKForObject(obj, c.Scheme()) + if err != nil { + return kutil.VerbUnchanged, errors.Wrapf(err, "failed to get GVK for object %T", obj) + } + + cur := obj.DeepCopyObject().(client.Object) + key := types.NamespacedName{ + Namespace: cur.GetNamespace(), + Name: cur.GetName(), + } + err = c.Get(ctx, key, cur) + if kerr.IsNotFound(err) { + klog.V(3).Infof("Creating %+v %s/%s.", gvk, key.Namespace, key.Name) + + createOpts := make([]client.CreateOption, 0, len(opts)) + for i := range opts { + if opt, ok := opts[i].(client.CreateOption); ok { + createOpts = append(createOpts, opt) + } + } + mod := transform(obj.DeepCopyObject().(client.Object), true) + err := c.Create(ctx, mod, createOpts...) + if err != nil { + return kutil.VerbUnchanged, err + } + + assign(obj, mod) + return kutil.VerbCreated, err + } else if err != nil { + return kutil.VerbUnchanged, err + } + + _, unstructuredObj := obj.(*unstructured.Unstructured) + + var patch client.Patch + if isOfficialTypes(gvk.Group) && !unstructuredObj { + patch = client.StrategicMergeFrom(cur) + } else { + patch = client.MergeFrom(cur) + } + mod := transform(cur.DeepCopyObject().(client.Object), false) + err = c.Patch(ctx, mod, patch, opts...) + if err != nil { + return kutil.VerbUnchanged, err + } + + assign(obj, mod) + return kutil.VerbPatched, nil +} + +func assign(target, src any) { + srcValue := reflect.ValueOf(src) + if srcValue.Kind() == reflect.Pointer { + srcValue = srcValue.Elem() + } + reflect.ValueOf(target).Elem().Set(srcValue) +} + +func PatchStatus(ctx context.Context, c client.Client, obj client.Object, transform TransformStatusFunc, opts ...client.SubResourcePatchOption) (kutil.VerbType, error) { + cur := obj.DeepCopyObject().(client.Object) + key := types.NamespacedName{ + Namespace: cur.GetNamespace(), + Name: cur.GetName(), + } + err := c.Get(ctx, key, cur) + if err != nil { + return kutil.VerbUnchanged, err + } + + // The body of the request was in an unknown format - + // accepted media types include: + // - application/json-patch+json, + // - application/merge-patch+json, + // - application/apply-patch+yaml + patch := client.MergeFrom(cur) + mod := transform(cur.DeepCopyObject().(client.Object)) + err = c.Status().Patch(ctx, mod, patch, opts...) + if err != nil { + return kutil.VerbUnchanged, err + } + assign(obj, mod) + return kutil.VerbPatched, nil +} + +func isOfficialTypes(group string) bool { + return !strings.ContainsRune(group, '.') +} + +func GetForGVR(ctx context.Context, c client.Client, gvr schema.GroupVersionResource, ref types.NamespacedName) (client.Object, error) { + gvk, err := c.RESTMapper().KindFor(gvr) + if err != nil { + return nil, err + } + o, err := c.Scheme().New(gvk) + if err != nil { + return nil, err + } + obj := o.(client.Object) + err = c.Get(ctx, ref, obj) + return obj, err +} + +func GetForGVK(ctx context.Context, c client.Client, gvk schema.GroupVersionKind, ref types.NamespacedName) (client.Object, error) { + if gvk.Version == "" { + mapping, err := c.RESTMapper().RESTMapping(gvk.GroupKind()) + if err != nil { + return nil, err + } + gvk = mapping.GroupVersionKind + } + o, err := c.Scheme().New(gvk) + if err != nil { + return nil, err + } + obj := o.(client.Object) + err = c.Get(ctx, ref, obj) + return obj, err +} diff --git a/vendor/kmodules.xyz/client-go/client/delegated.go b/vendor/kmodules.xyz/client-go/client/delegated.go new file mode 100644 index 000000000..fd36791a6 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/delegated.go @@ -0,0 +1,193 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 client + +import ( + "context" + "strings" + + apiutil2 "kmodules.xyz/client-go/client/apiutil" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + restclient "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +// Adapted from https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/client/split.go +// Uses discovery apis to correctly detect if an api type can be cached. + +// NewDelegatingClientInput encapsulates the input parameters to create a new delegating client. +type NewDelegatingClientInput struct { + CacheReader client.Reader + Client client.Client + UncachedObjects []client.Object + CacheUnstructured bool + Cachable apiutil2.Cachable +} + +// NewDelegatingClient creates a new delegating client. +// +// A delegating client forms a Client by composing separate reader, writer and +// statusclient interfaces. This way, you can have an Client that reads from a +// cache and writes to the API server. +func NewDelegatingClient(in NewDelegatingClientInput) (client.Client, error) { + uncachedGVKs := map[schema.GroupVersionKind]struct{}{} + for _, obj := range in.UncachedObjects { + gvk, err := apiutil.GVKForObject(obj, in.Client.Scheme()) + if err != nil { + return nil, err + } + uncachedGVKs[gvk] = struct{}{} + } + + return &delegatingClient{ + scheme: in.Client.Scheme(), + mapper: in.Client.RESTMapper(), + Reader: &delegatingReader{ + CacheReader: in.CacheReader, + ClientReader: in.Client, + scheme: in.Client.Scheme(), + uncachedGVKs: uncachedGVKs, + cacheUnstructured: in.CacheUnstructured, + cachable: in.Cachable, + }, + Writer: in.Client, + StatusClient: in.Client, + SubResourceClientConstructor: in.Client, + }, nil +} + +type delegatingClient struct { + client.Reader + client.Writer + client.StatusClient + client.SubResourceClientConstructor + + scheme *runtime.Scheme + mapper meta.RESTMapper +} + +// GroupVersionKindFor returns the GroupVersionKind for the given object. +func (d *delegatingClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { + return apiutil.GVKForObject(obj, d.scheme) +} + +// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced. +func (d *delegatingClient) IsObjectNamespaced(obj runtime.Object) (bool, error) { + return apiutil.IsObjectNamespaced(obj, d.scheme, d.mapper) +} + +// Scheme returns the scheme this client is using. +func (d *delegatingClient) Scheme() *runtime.Scheme { + return d.scheme +} + +// RESTMapper returns the rest mapper this client is using. +func (d *delegatingClient) RESTMapper() meta.RESTMapper { + return d.mapper +} + +// delegatingReader forms a Reader that will cause Get and List requests for +// unstructured types to use the ClientReader while requests for any other type +// of object with use the CacheReader. This avoids accidentally caching the +// entire cluster in the common case of loading arbitrary unstructured objects +// (e.g. from OwnerReferences). +type delegatingReader struct { + CacheReader client.Reader + ClientReader client.Reader + + uncachedGVKs map[schema.GroupVersionKind]struct{} + scheme *runtime.Scheme + cacheUnstructured bool + cachable apiutil2.Cachable +} + +func (d *delegatingReader) shouldBypassCache(obj runtime.Object) (bool, error) { + gvk, err := apiutil.GVKForObject(obj, d.scheme) + if err != nil { + return false, err + } + // TODO: this is producing unsafe guesses that don't actually work, + // but it matches ~99% of the cases out there. + if meta.IsListType(obj) { + gvk.Kind = strings.TrimSuffix(gvk.Kind, "List") + } + if d.cachable != nil { + canCache, err := d.cachable.GVK(gvk) + if err != nil || !canCache { + return true, err + } + } + if _, isUncached := d.uncachedGVKs[gvk]; isUncached { + return true, nil + } + if !d.cacheUnstructured { + _, isUnstructured := obj.(*unstructured.Unstructured) + _, isUnstructuredList := obj.(*unstructured.UnstructuredList) + return isUnstructured || isUnstructuredList, nil + } + return false, nil +} + +// Get retrieves an obj for a given object key from the Kubernetes Cluster. +func (d *delegatingReader) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + if isUncached, err := d.shouldBypassCache(obj); err != nil { + return err + } else if isUncached { + return d.ClientReader.Get(ctx, key, obj, opts...) + } + return d.CacheReader.Get(ctx, key, obj, opts...) +} + +// List retrieves list of objects for a given namespace and list options. +func (d *delegatingReader) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + if isUncached, err := d.shouldBypassCache(list); err != nil { + return err + } else if isUncached { + return d.ClientReader.List(ctx, list, opts...) + } + return d.CacheReader.List(ctx, list, opts...) +} + +func (d *delegatingClient) SubResource(subResource string) client.SubResourceClient { + return d.SubResourceClientConstructor.SubResource(subResource) +} + +func NewClient(config *restclient.Config, options client.Options) (client.Client, error) { + c, err := client.New(config, options) + if err != nil { + return nil, err + } + cachable, err := apiutil2.NewDynamicCachable(config) + if err != nil { + return nil, err + } + co := NewDelegatingClientInput{ + Client: c, + Cachable: cachable, + } + if options.Cache != nil { + co.CacheReader = options.Cache.Reader + co.UncachedObjects = options.Cache.DisableFor + co.CacheUnstructured = options.Cache.Unstructured // cache unstructured objects + } + return NewDelegatingClient(co) +} diff --git a/vendor/kmodules.xyz/client-go/client/sa-token.go b/vendor/kmodules.xyz/client-go/client/sa-token.go new file mode 100644 index 000000000..866a86f38 --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/sa-token.go @@ -0,0 +1,115 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 client + +import ( + "context" + "time" + + core_util "kmodules.xyz/client-go/core/v1" + meta_util "kmodules.xyz/client-go/meta" + + "github.com/pkg/errors" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilrand "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + kutil "kmodules.xyz/client-go" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#token-controller +func getServiceAccountTokenSecret(kc client.Client, sa client.ObjectKey) (*core.Secret, error) { + var list core.SecretList + err := kc.List(context.TODO(), &list, client.InNamespace(sa.Namespace)) + if err != nil { + return nil, err + } + if len(list.Items) == 0 { + return nil, errors.New("token secret still haven't created yet") + } + for _, s := range list.Items { + if s.Type == core.SecretTypeServiceAccountToken && + s.Annotations[core.ServiceAccountNameKey] == sa.Name { + + _, caFound := s.Data["ca.crt"] + _, tokenFound := s.Data["token"] + if caFound && tokenFound { + return &s, nil + } + } + } + return nil, errors.New("token secret is not ready yet") +} + +const ( + RetryTimeout = 10 * time.Second +) + +func tryGetServiceAccountTokenSecret(kc client.Client, sa client.ObjectKey) (secret *core.Secret, err error) { + err = wait.PollUntilContextTimeout(context.Background(), kutil.RetryInterval, RetryTimeout, true, func(ctx context.Context) (bool, error) { + var e2 error + secret, e2 = getServiceAccountTokenSecret(kc, sa) + if e2 == nil { + return true, nil + } + klog.V(5).Infof("trying to get token secret for service account %s", sa) + return false, nil + }) + return +} + +func GetServiceAccountTokenSecret(kc client.Client, sa client.ObjectKey) (*core.Secret, error) { + secret, err := tryGetServiceAccountTokenSecret(kc, sa) + if err == nil { + klog.V(5).Infof("secret found for ServiceAccount %s", sa) + return secret, nil + } + + var saObj core.ServiceAccount + err = kc.Get(context.TODO(), sa, &saObj) + if err != nil { + return nil, errors.Wrapf(err, "failed to get ServiceAccount %s", sa) + } + + secretName := sa.Name + "-token-" + utilrand.String(6) + secret = &core.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: sa.Namespace, + }, + } + vt, err := CreateOrPatch(context.TODO(), kc, secret, func(obj client.Object, createOp bool) client.Object { + in := obj.(*core.Secret) + + in.Type = core.SecretTypeServiceAccountToken + ref := metav1.NewControllerRef(&saObj, core.SchemeGroupVersion.WithKind("ServiceAccount")) + core_util.EnsureOwnerReference(in, ref) + in.Annotations = meta_util.OverwriteKeys(in.Annotations, map[string]string{ + core.ServiceAccountNameKey: sa.Name, + }) + + return in + }) + if err != nil { + return nil, err + } + klog.Infof("%s Secret %s/%s", vt, secret.Namespace, secret.Name) + + return tryGetServiceAccountTokenSecret(kc, sa) +} diff --git a/vendor/kmodules.xyz/client-go/client/typeclient.go b/vendor/kmodules.xyz/client-go/client/typeclient.go new file mode 100644 index 000000000..e36cacbfa --- /dev/null +++ b/vendor/kmodules.xyz/client-go/client/typeclient.go @@ -0,0 +1,461 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 client + +import ( + "context" + "fmt" + "strings" + "sync" + + apiutil2 "kmodules.xyz/client-go/client/apiutil" + + apimeta "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + restclient "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +const listType = "List" + +type typedClient struct { + c client.Client + cachable apiutil2.Cachable + *readerWrapper +} + +var ( + _ client.Reader = &typedClient{} + _ client.Writer = &typedClient{} + _ client.StatusClient = &typedClient{} + _ apiutil2.Cachable = &typedClient{} +) + +// GroupVersionKindFor returns the GroupVersionKind for the given object. +func (d *typedClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { + return d.c.GroupVersionKindFor(obj) +} + +// IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced. +func (d *typedClient) IsObjectNamespaced(obj runtime.Object) (bool, error) { + return d.c.IsObjectNamespaced(obj) +} + +// Scheme returns the scheme this client is using. +func (d *typedClient) Scheme() *runtime.Scheme { + return d.c.Scheme() +} + +// RESTMapper returns the rest this client is using. +func (d *typedClient) RESTMapper() apimeta.RESTMapper { + return d.c.RESTMapper() +} + +type readerWrapper struct { + c client.Reader + scheme *runtime.Scheme + typeMap map[schema.GroupVersionKind]schema.GroupVersionKind + mu sync.Mutex +} + +var _ client.Reader = &readerWrapper{} + +func (d *readerWrapper) getMappedType(gvk schema.GroupVersionKind) (schema.GroupVersionKind, bool) { + d.mu.Lock() + rawGVK, found := d.typeMap[gvk] + d.mu.Unlock() + return rawGVK, found +} + +func (d *readerWrapper) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + gvk, err := apiutil.GVKForObject(obj, d.scheme) + if err != nil { + return err + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.Get(ctx, key, obj, opts...) + } + + ll, err := d.scheme.New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + err = d.c.Get(ctx, key, llo, opts...) + if err != nil { + return err + } + + return d.scheme.Convert(llo, obj, nil) +} + +func (d *readerWrapper) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + gvk, err := apiutil.GVKForObject(list, d.scheme) + if err != nil { + return err + } + if strings.HasSuffix(gvk.Kind, listType) && apimeta.IsListType(list) { + gvk.Kind = gvk.Kind[:len(gvk.Kind)-4] + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.List(ctx, list, opts...) + } + + listGVK := rawGVK + listGVK.Kind += listType + + ll, err := d.scheme.New(listGVK) + if err != nil { + return err + } + llo := ll.(client.ObjectList) + err = d.c.List(ctx, llo, opts...) + if err != nil { + return err + } + + list.SetResourceVersion(llo.GetResourceVersion()) + list.SetContinue(llo.GetContinue()) + list.SetSelfLink(llo.GetSelfLink()) + list.SetRemainingItemCount(llo.GetRemainingItemCount()) + + items := make([]runtime.Object, 0, apimeta.LenList(llo)) + err = apimeta.EachListItem(llo, func(object runtime.Object) error { + d2, err := d.scheme.New(gvk) + if err != nil { + return err + } + err = d.scheme.Convert(object, d2, nil) + if err != nil { + return err + } + items = append(items, d2) + return nil + }) + if err != nil { + return err + } + return apimeta.SetList(list, items) +} + +func (d *typedClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + gvk, err := apiutil.GVKForObject(obj, d.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.Create(ctx, obj, opts...) + } + + ll, err := d.c.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + err = d.Scheme().Convert(obj, llo, nil) + if err != nil { + return err + } + return d.c.Create(ctx, llo, opts...) +} + +func (d *typedClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + gvk, err := apiutil.GVKForObject(obj, d.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.Delete(ctx, obj, opts...) + } + + ll, err := d.c.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + llo.SetNamespace(obj.GetNamespace()) + llo.SetName(obj.GetName()) + llo.SetLabels(obj.GetLabels()) + return d.c.Delete(ctx, llo, opts...) +} + +func (d *typedClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + gvk, err := apiutil.GVKForObject(obj, d.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.Update(ctx, obj, opts...) + } + + ll, err := d.c.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + err = d.Scheme().Convert(obj, llo, nil) + if err != nil { + return err + } + return d.c.Update(ctx, llo, opts...) +} + +func (d *typedClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + gvk, err := apiutil.GVKForObject(obj, d.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.Patch(ctx, obj, patch, opts...) + } + + ll, err := d.c.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + llo.SetNamespace(obj.GetNamespace()) + llo.SetName(obj.GetName()) + llo.SetLabels(obj.GetLabels()) + return d.c.Patch(ctx, llo, patch, opts...) +} + +func (d *typedClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + gvk, err := apiutil.GVKForObject(obj, d.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := d.getMappedType(gvk) + if !found { + return d.c.DeleteAllOf(ctx, obj, opts...) + } + + ll, err := d.c.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + llo.SetNamespace(obj.GetNamespace()) + llo.SetName(obj.GetName()) + llo.SetLabels(obj.GetLabels()) + return d.c.DeleteAllOf(ctx, llo, opts...) +} + +func (d *typedClient) Status() client.StatusWriter { + return &typedStatusWriter{client: d} +} + +// typedStatusWriter is client.StatusWriter that writes status subresource. +type typedStatusWriter struct { + client *typedClient +} + +// ensure typedStatusWriter implements client.StatusWriter. +var _ client.StatusWriter = &typedStatusWriter{} + +func (sw *typedStatusWriter) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error { + gvk, err := apiutil.GVKForObject(obj, sw.client.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := sw.client.getMappedType(gvk) + if !found { + return sw.client.c.Status().Create(ctx, obj, subResource, opts...) + } + + ll, err := sw.client.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + err = sw.client.Scheme().Convert(obj, llo, nil) + if err != nil { + return err + } + return sw.client.c.Status().Create(ctx, llo, subResource, opts...) +} + +func (sw *typedStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + gvk, err := apiutil.GVKForObject(obj, sw.client.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := sw.client.getMappedType(gvk) + if !found { + return sw.client.c.Status().Update(ctx, obj, opts...) + } + + ll, err := sw.client.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + err = sw.client.Scheme().Convert(obj, llo, nil) + if err != nil { + return err + } + return sw.client.c.Status().Update(ctx, llo, opts...) +} + +func (sw *typedStatusWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { + gvk, err := apiutil.GVKForObject(obj, sw.client.c.Scheme()) + if err != nil { + return err + } + + rawGVK, found := sw.client.getMappedType(gvk) + if !found { + return sw.client.c.Status().Patch(ctx, obj, patch, opts...) + } + + ll, err := sw.client.c.Scheme().New(rawGVK) + if err != nil { + return err + } + llo := ll.(client.Object) + llo.SetNamespace(obj.GetNamespace()) + llo.SetName(obj.GetName()) + llo.SetLabels(obj.GetLabels()) + return sw.client.c.Status().Patch(ctx, llo, patch, opts...) +} + +func (d *typedClient) SubResource(subResource string) client.SubResourceClient { + return d.c.SubResource(subResource) +} + +func (d *typedClient) GVK(gvk schema.GroupVersionKind) (bool, error) { + d.mu.Lock() + rawGVK, found := d.typeMap[gvk] + d.mu.Unlock() + + if !found { + return d.cachable.GVK(gvk) + } + return d.cachable.GVK(rawGVK) +} + +func (d *typedClient) GVR(gvr schema.GroupVersionResource) (bool, error) { + gvk, err := d.c.RESTMapper().KindFor(schema.GroupVersionResource{ + Group: gvr.Group, + Version: "", + Resource: gvr.Resource, + }) + if err != nil { + return false, err + } + return d.GVK(gvk) +} + +func BuildTypeMap(kc client.Client, gvks ...schema.GroupVersionKind) (map[schema.GroupVersionKind]schema.GroupVersionKind, error) { + tm := map[schema.GroupVersionKind]schema.GroupVersionKind{} + + for _, gvk := range gvks { + mappings, err := kc.RESTMapper().RESTMappings(gvk.GroupKind()) + if err != nil { + return nil, err + } + + var found bool + for _, mapping := range mappings { + if mapping.GroupVersionKind == gvk { + found = true + break + } + } + if !found { + for _, mapping := range mappings { + + in, err := kc.Scheme().New(gvk) + if err != nil { + return nil, err + } + out, err := kc.Scheme().New(mapping.GroupVersionKind) + if err != nil { + return nil, err + } + if err := kc.Scheme().Convert(in, out, nil); err == nil { + tm[gvk] = mapping.GroupVersionKind + found = true + break + } + } + } + if !found { + return nil, fmt.Errorf("type mapping not found for %+v", gvk) + } + } + + return tm, nil +} + +func NewAutoConvertClient(gvks ...schema.GroupVersionKind) client.NewClientFunc { + return func(config *restclient.Config, options client.Options) (client.Client, error) { + c, err := client.New(config, options) + if err != nil { + return nil, err + } + cachable, err := apiutil2.NewDynamicCachable(config) + if err != nil { + return nil, err + } + tm, err := BuildTypeMap(c, gvks...) + if err != nil { + return nil, err + } + tc := &typedClient{ + c: c, + cachable: cachable, + readerWrapper: &readerWrapper{ + c: c, + scheme: c.Scheme(), + typeMap: tm, + }, + } + + co := NewDelegatingClientInput{ + Client: tc, + Cachable: tc, + } + if options.Cache != nil { + co.CacheReader = &readerWrapper{ + c: options.Cache.Reader, + scheme: c.Scheme(), + typeMap: tm, + } + co.UncachedObjects = options.Cache.DisableFor + co.CacheUnstructured = options.Cache.Unstructured // cache unstructured objects + } + return NewDelegatingClient(co) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 83b71e3e5..6ce6e0dea 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -740,6 +740,9 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value +# github.com/google/go-containerregistry v0.19.0 +## explicit; go 1.18 +github.com/google/go-containerregistry/pkg/name # github.com/google/gofuzz v1.2.0 ## explicit; go 1.12 github.com/google/gofuzz @@ -826,6 +829,9 @@ github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util github.com/onsi/gomega/types +# github.com/opencontainers/go-digest v1.0.0 +## explicit; go 1.13 +github.com/opencontainers/go-digest # github.com/opensearch-project/opensearch-go v1.1.0 ## explicit; go 1.11 github.com/opensearch-project/opensearch-go @@ -1465,6 +1471,8 @@ kmodules.xyz/client-go/api/v1 kmodules.xyz/client-go/apiextensions kmodules.xyz/client-go/apiextensions/v1 kmodules.xyz/client-go/apps/v1 +kmodules.xyz/client-go/client +kmodules.xyz/client-go/client/apiutil kmodules.xyz/client-go/conditions kmodules.xyz/client-go/core/v1 kmodules.xyz/client-go/discovery