diff --git a/go.mod b/go.mod index 060479d7f..149bf9ed5 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/modern-go/reflect2 v1.0.1 // indirect github.com/operator-framework/go-appr v0.0.0-20180917210448-f2aef88446f2 github.com/operator-framework/operator-lifecycle-manager v0.0.0-20190125151539-1e295784b30a - github.com/operator-framework/operator-marketplace v0.0.0-20190212161948-a7ca81b96ad9 + github.com/operator-framework/operator-marketplace v0.0.0-20190216021216-57300a3ef3ba github.com/sirupsen/logrus v1.2.0 github.com/soheilhy/cmux v0.1.4 // indirect github.com/spf13/cobra v0.0.3 diff --git a/go.sum b/go.sum index ef796621e..cb7961947 100644 --- a/go.sum +++ b/go.sum @@ -141,6 +141,8 @@ github.com/operator-framework/operator-marketplace v0.0.0-20190208230340-d06f7b3 github.com/operator-framework/operator-marketplace v0.0.0-20190208230340-d06f7b349013/go.mod h1:msZSL8pXwzQjB+hU+awVrZQw94IwJi3sNZVD3NoESIs= github.com/operator-framework/operator-marketplace v0.0.0-20190212161948-a7ca81b96ad9 h1:VjGYvB+9cqsf0vgO7npB1bwAIslvLFqqL1ydX9ogCRM= github.com/operator-framework/operator-marketplace v0.0.0-20190212161948-a7ca81b96ad9/go.mod h1:msZSL8pXwzQjB+hU+awVrZQw94IwJi3sNZVD3NoESIs= +github.com/operator-framework/operator-marketplace v0.0.0-20190216021216-57300a3ef3ba h1:47MQUQRBZqwyTPLEHoFlbGRv63p0OvxpPp5g6FUQXQs= +github.com/operator-framework/operator-marketplace v0.0.0-20190216021216-57300a3ef3ba/go.mod h1:msZSL8pXwzQjB+hU+awVrZQw94IwJi3sNZVD3NoESIs= github.com/operator-framework/operator-registry v1.0.1/go.mod h1:1xEdZjjUg2hPEd52LG3YQ0jtwiwEGdm98S1TH5P4RAA= github.com/operator-framework/operator-registry v1.0.4/go.mod h1:hve6YwcjM2nGVlscLtNsp9sIIBkNZo6jlJgzWw7vP9s= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= diff --git a/pkg/apprclient/apprclient.go b/pkg/apprclient/apprclient.go index fddf323a9..929ad05ed 100644 --- a/pkg/apprclient/apprclient.go +++ b/pkg/apprclient/apprclient.go @@ -14,21 +14,40 @@ func NewClientFactory() ClientFactory { return &factory{} } +type Options struct { + // Source refers to the URL of the remote app registry server. + Source string + + // AuthToken refers to the authorization token required to access operator + // manifest in private repositories. + // + // If not set, it is assumed that the remote registry is public. + AuthToken string +} + +// ClientFactory is an interface that wraps the New method. type ClientFactory interface { - // New returns a new instance of appregistry Client from given source and type - New(sourceType, source string) (Client, error) + // New returns a new instance of appregistry Client from the specified source. + New(options Options) (Client, error) } type factory struct{} -func (f *factory) New(sourceType, source string) (Client, error) { - u, err := url.Parse(source) +func (f *factory) New(options Options) (Client, error) { + u, err := url.Parse(options.Source) if err != nil { return nil, err } transport := httptransport.New(u.Host, u.Path, []string{u.Scheme}) transport.Consumers["application/x-gzip"] = runtime.ByteStreamConsumer() + + // If a token has been specified then we should pass it along in the headers + if options.AuthToken != "" { + tokenAuthWriter := httptransport.APIKeyAuth("Authorization", "header", options.AuthToken) + transport.DefaultAuthentication = tokenAuthWriter + } + c := apprclient.New(transport, strfmt.Default) return &client{ diff --git a/pkg/appregistry/appregistry.go b/pkg/appregistry/appregistry.go index fdf6c1545..62ac77421 100644 --- a/pkg/appregistry/appregistry.go +++ b/pkg/appregistry/appregistry.go @@ -6,12 +6,18 @@ import ( marketplace "github.com/operator-framework/operator-marketplace/pkg/client/clientset/versioned" "github.com/operator-framework/operator-registry/pkg/sqlite" "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) func NewLoader(kubeconfig string, logger *logrus.Entry) (*AppregistryLoader, error) { - client, err := NewClient(kubeconfig, logger) + marketplaceClient, err := NewClient(kubeconfig, logger) + if err != nil { + return nil, err + } + + kubeClient, err := NewKubeClient(kubeconfig, logger) if err != nil { return nil, err } @@ -20,8 +26,9 @@ func NewLoader(kubeconfig string, logger *logrus.Entry) (*AppregistryLoader, err logger: logger, input: &inputParser{}, downloader: &downloader{ - logger: logger, - client: client, + logger: logger, + marketplaceClient: marketplaceClient, + kubeClient: *kubeClient, }, merger: &merger{ logger: logger, @@ -100,3 +107,23 @@ func NewClient(kubeconfig string, logger *logrus.Entry) (clientset marketplace.I clientset, err = marketplace.NewForConfig(config) return } + +func NewKubeClient(kubeconfig string, logger *logrus.Entry) (clientset *kubernetes.Clientset, err error) { + var config *rest.Config + + if kubeconfig != "" { + logger.Infof("Loading kube client config from path %q", kubeconfig) + config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + } else { + logger.Infof("Using in-cluster kube client config") + config, err = rest.InClusterConfig() + } + + if err != nil { + err = fmt.Errorf("Cannot load config for REST client: %v", err) + return + } + + clientset, err = kubernetes.NewForConfig(config) + return +} diff --git a/pkg/appregistry/downloader.go b/pkg/appregistry/downloader.go index f99201e49..81ebc3a4f 100644 --- a/pkg/appregistry/downloader.go +++ b/pkg/appregistry/downloader.go @@ -10,6 +10,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/client-go/kubernetes" ) // downloadItem encapsulates the data that is needed to download a specific repository. @@ -20,6 +21,9 @@ type downloadItem struct { // Spec refers to the remote appregistry URL and remote registry namespace. Spec *v1alpha1.OperatorSourceSpec + + // Namespace of the operatorsource that provided the information + OpsrcNamespace string } func (d *downloadItem) String() string { @@ -27,8 +31,9 @@ func (d *downloadItem) String() string { } type downloader struct { - logger *logrus.Entry - client marketplace.Interface + logger *logrus.Entry + marketplaceClient marketplace.Interface + kubeClient kubernetes.Clientset } // Download downloads manifest(s) associated with the specified package(s) from @@ -93,6 +98,7 @@ func (d *downloader) Prepare(input *Input) (items []*downloadItem, err error) { itemMap[key] = &downloadItem{ RepositoryMetadata: metadata, Spec: spec, + OpsrcNamespace: source.Namespace, } // Remove the package specified since it has been resolved. @@ -128,7 +134,15 @@ func (d *downloader) DownloadRepositories(items []*downloadItem) (manifests []*a factory := apprclient.NewClientFactory() - client, err := factory.New("appregistry", endpoint) + options, err := d.SetupRegistryOptions(item.Spec, item.OpsrcNamespace) + if err != nil { + allErrors = append(allErrors, err) + d.logger.Infof("skipping repository: %s", item.RepositoryMetadata) + + continue + } + + client, err := factory.New(*options) if err != nil { allErrors = append(allErrors, err) d.logger.Infof("skipping repository: %s", item.RepositoryMetadata) @@ -159,13 +173,19 @@ func (d *downloader) DownloadRepositories(items []*downloadItem) (manifests []*a // in the cluster and the list of repositories in remote registry associated // with it. func (d *downloader) QuerySource(key *types.NamespacedName) (spec *v1alpha1.OperatorSourceSpec, repositories []*apprclient.RegistryMetadata, err error) { - opsrc, err := d.client.MarketplaceV1alpha1().OperatorSources(key.Namespace).Get(key.Name, metav1.GetOptions{}) + opsrc, err := d.marketplaceClient.MarketplaceV1alpha1().OperatorSources(key.Namespace).Get(key.Name, metav1.GetOptions{}) if err != nil { return } factory := apprclient.NewClientFactory() - client, err := factory.New("appregistry", opsrc.Spec.Endpoint) + + options, err := d.SetupRegistryOptions(&opsrc.Spec, key.Namespace) + if err != nil { + return + } + + client, err := factory.New(*options) if err != nil { return } @@ -178,3 +198,24 @@ func (d *downloader) QuerySource(key *types.NamespacedName) (spec *v1alpha1.Oper spec = &opsrc.Spec return } + +// SetupRegistryOptions generates an Options object based on the OperatorSource spec. It passes along +// the opsrc endpoint and, if defined, retrieves the authorization token from the specified Secret +// object. +func (d *downloader) SetupRegistryOptions(spec *v1alpha1.OperatorSourceSpec, namespace string) (*apprclient.Options, error) { + options := &apprclient.Options{ + Source: spec.Endpoint, + } + + auth := spec.AuthorizationToken + if auth.SecretName != "" { + secret, err := d.kubeClient.CoreV1().Secrets(namespace).Get(auth.SecretName, metav1.GetOptions{}) + if err != nil { + return options, err + } + + options.AuthToken = string(secret.Data["token"]) + } + + return options, nil +} diff --git a/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/catalogsourceconfig_types.go b/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/catalogsourceconfig_types.go index dc5c22e52..0ad685045 100644 --- a/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/catalogsourceconfig_types.go +++ b/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/catalogsourceconfig_types.go @@ -5,6 +5,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" ) const ( @@ -100,3 +101,25 @@ func (csc *CatalogSourceConfig) EnsurePublisher() { func (csc *CatalogSourceConfig) GetPackageIDs() []string { return strings.Split(csc.Spec.Packages, ",") } + +// GetTargetNamespace returns the TargetNamespace where the OLM resources will +// be created. +func (csc *CatalogSourceConfig) GetTargetNamespace() string { + return csc.Spec.TargetNamespace +} + +// RemoveOwner removes the owner specified in ownerUID from OwnerReference of +// the CatalogSourceConfig object. +func (csc *CatalogSourceConfig) RemoveOwner(ownerUID types.UID) { + owners := make([]metav1.OwnerReference, 0) + + for _, owner := range csc.GetOwnerReferences() { + if owner.UID == ownerUID { + continue + } + + owners = append(owners, owner) + } + + csc.SetOwnerReferences(owners) +} diff --git a/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/operatorsource_types.go b/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/operatorsource_types.go index 4c6e060f0..c11369d28 100644 --- a/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/operatorsource_types.go +++ b/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/operatorsource_types.go @@ -13,19 +13,6 @@ const ( OpSrcFinalizer = "finalizer.operatorsources.marketplace.redhat.com" ) -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// OperatorSource is the Schema for the operatorsources API -// +k8s:openapi-gen=true -type OperatorSource struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec OperatorSourceSpec `json:"spec,omitempty"` - Status OperatorSourceStatus `json:"status,omitempty"` -} - // Only type definitions go into this file. // All other constructs (constants, variables, receiver functions and such) // related to OperatorSource type should be added to operatorsource.go file. @@ -39,6 +26,16 @@ type OperatorSourceList struct { Items []OperatorSource `json:"items"` } +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// OperatorSource is the Schema for the operatorsources API +// +k8s:openapi-gen=true +type OperatorSource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec OperatorSourceSpec `json:"spec,omitempty"` + Status OperatorSourceStatus `json:"status,omitempty"` +} + // OperatorSourceSpec defines the desired state of OperatorSource type OperatorSourceSpec struct { // Type of operator source. @@ -53,6 +50,10 @@ type OperatorSourceSpec struct { // Please note that this is not a k8s namespace. RegistryNamespace string `json:"registryNamespace,omitempty"` + // AuthorizationToken is the authorization token used to access private + // repositories in remote registry associated with the operator source. + AuthorizationToken OperatorSourceAuthorizationToken `json:"authorizationToken,omitempty"` + // DisplayName is passed along to the CatalogSourceConfig to be used // by the resulting CatalogSource to be used as a pretty name. DisplayName string `json:"displayName,omitempty"` @@ -63,6 +64,13 @@ type OperatorSourceSpec struct { Publisher string `json:"publisher,omitempty"` } +// OperatorSourceAuthentication refers to a kubernetes Secret object that +// contains authorization token required to access private repositories. +type OperatorSourceAuthorizationToken struct { + // SecretName is the name of the kubernetes Secret object. + SecretName string `json:"secretName,omitempty"` +} + // OperatorSourceStatus defines the observed state of OperatorSource type OperatorSourceStatus struct { // Current phase of the OperatorSource object diff --git a/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/zz_generated.deepcopy.go index 77f5565ef..c4f8adfba 100644 --- a/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1/zz_generated.deepcopy.go @@ -165,6 +165,22 @@ func (in *OperatorSource) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OperatorSourceAuthorizationToken) DeepCopyInto(out *OperatorSourceAuthorizationToken) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorSourceAuthorizationToken. +func (in *OperatorSourceAuthorizationToken) DeepCopy() *OperatorSourceAuthorizationToken { + if in == nil { + return nil + } + out := new(OperatorSourceAuthorizationToken) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorSourceList) DeepCopyInto(out *OperatorSourceList) { *out = *in @@ -201,6 +217,7 @@ func (in *OperatorSourceList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorSourceSpec) DeepCopyInto(out *OperatorSourceSpec) { *out = *in + out.AuthorizationToken = in.AuthorizationToken return } diff --git a/vendor/modules.txt b/vendor/modules.txt index 1061186c0..8d1e5660c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -107,7 +107,7 @@ github.com/operator-framework/go-appr/appregistry/info github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1 github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators -# github.com/operator-framework/operator-marketplace v0.0.0-20190212161948-a7ca81b96ad9 +# github.com/operator-framework/operator-marketplace v0.0.0-20190216021216-57300a3ef3ba github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1 github.com/operator-framework/operator-marketplace/pkg/client/clientset/versioned github.com/operator-framework/operator-marketplace/pkg/client/clientset/versioned/typed/marketplace/v1alpha1