From 5e657f876b29f5a0ad9e3e46716919bde347f992 Mon Sep 17 00:00:00 2001 From: Lukas Krejci Date: Tue, 21 Sep 2021 21:00:53 +0200 Subject: [PATCH] feat: Syncing of proxy settings and self-signed cert to the user's workspace namespace (#1027) Syncing of proxy settings and self-signed cert to the user's workspace namespace. Co-authored-by: Michal Vala Co-authored-by: Serhii Leshchenko --- .github/bin/common.sh | 33 +- api/v2alpha1/checluster_types.go | 2 +- .../che-operator.clusterserviceversion.yaml | 7 +- .../che-operator.clusterserviceversion.yaml | 8 +- config/manager/manager.yaml | 2 +- config/rbac/cluster_role.yaml | 2 + controllers/che/checluster_controller.go | 2 +- controllers/che/proxy.go | 2 +- controllers/che/proxy_test.go | 10 +- controllers/devworkspace/controller.go | 19 +- .../devworkspace/solver/endpoint_exposer.go | 4 +- controllers/devworkspace/util.go | 38 + controllers/usernamespace/controller.go | 404 +++++ controllers/usernamespace/controller_test.go | 330 ++++ controllers/usernamespace/namespacecache.go | 133 ++ .../usernamespace/namespacecache_test.go | 136 ++ main.go | 15 + pkg/deploy/configmap.go | 6 +- pkg/deploy/secret.go | 4 +- pkg/deploy/sync.go | 10 + .../openshift/api/project/v1/doc.go | 8 + .../openshift/api/project/v1/generated.pb.go | 1346 +++++++++++++++++ .../openshift/api/project/v1/generated.proto | 77 + .../openshift/api/project/v1/legacy.go | 23 + .../openshift/api/project/v1/register.go | 40 + .../openshift/api/project/v1/types.go | 93 ++ .../api/project/v1/zz_generated.deepcopy.go | 141 ++ .../v1/zz_generated.swagger_doc_generated.go | 65 + vendor/modules.txt | 1 + 29 files changed, 2925 insertions(+), 36 deletions(-) create mode 100644 controllers/devworkspace/util.go create mode 100644 controllers/usernamespace/controller.go create mode 100644 controllers/usernamespace/controller_test.go create mode 100644 controllers/usernamespace/namespacecache.go create mode 100644 controllers/usernamespace/namespacecache_test.go create mode 100644 vendor/github.com/openshift/api/project/v1/doc.go create mode 100644 vendor/github.com/openshift/api/project/v1/generated.pb.go create mode 100644 vendor/github.com/openshift/api/project/v1/generated.proto create mode 100644 vendor/github.com/openshift/api/project/v1/legacy.go create mode 100644 vendor/github.com/openshift/api/project/v1/register.go create mode 100644 vendor/github.com/openshift/api/project/v1/types.go create mode 100644 vendor/github.com/openshift/api/project/v1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/openshift/api/project/v1/zz_generated.swagger_doc_generated.go diff --git a/.github/bin/common.sh b/.github/bin/common.sh index 8bfa0b3f4..483bc1afb 100755 --- a/.github/bin/common.sh +++ b/.github/bin/common.sh @@ -167,24 +167,49 @@ installYq() { # Graps Eclipse Che logs collectLogs() { mkdir -p ${ARTIFACTS_DIR} - chectl server:logs --chenamespace=${NAMESPACE} --directory=${ARTIFACTS_DIR} - set +x + set +e + chectl server:logs --chenamespace=${NAMESPACE} --directory=${ARTIFACTS_DIR} + collectDevworkspaceOperatorLogs oc get events -n ${DEVWORKSPACE_CONTROLLER_TEST_NAMESPACE} > ${ARTIFACTS_DIR}/events-${DEVWORKSPACE_CONTROLLER_TEST_NAMESPACE}.txt oc get events -n ${DEVWORKSPACE_CHE_OPERATOR_TEST_NAMESPACE} > ${ARTIFACTS_DIR}/events-${DEVWORKSPACE_CHE_OPERATOR_TEST_NAMESPACE}.txt - set -x + set -e +} + +collectDevworkspaceOperatorLogs() { + mkdir -p ${ARTIFACTS_DIR}/devworkspace-operator + + oc get events -n devworkspace-controller > ${ARTIFACTS_DIR}/events-devworkspace-controller.txt + + #determine the name of the devworkspace controller manager pod + local CONTROLLER_POD_NAME=$(oc get pods -n devworkspace-controller -l app.kubernetes.io/name=devworkspace-controller -o json | jq -r '.items[0].metadata.name') + local WEBHOOK_SVR_POD_NAME=$(oc get pods -n devworkspace-controller -l app.kubernetes.io/name=devworkspace-webhook-server -o json | jq -r '.items[0].metadata.name') + + # save the logs of all the containers in the DWO pod + for container in $(oc get pod -n devworkspace-controller ${CONTROLLER_POD_NAME} -o json | jq -r '.spec.containers[] | .name'); do + mkdir -p ${ARTIFACTS_DIR}/devworkspace-operator/${CONTROLLER_POD_NAME} + oc logs -n devworkspace-controller deployment/devworkspace-controller-manager -c ${container} > ${ARTIFACTS_DIR}/devworkspace-operator/${CONTROLLER_POD_NAME}/${container}.log + done + + for container in $(oc get pod -n devworkspace-controller ${WEBHOOK_SVR_POD_NAME} -o json | jq -r '.spec.containers[] | .name'); do + mkdir -p ${ARTIFACTS_DIR}/devworkspace-operator/${WEBHOOK_SVR_POD_NAME} + oc logs -n devworkspace-controller deployment/devworkspace-webhook-server -c ${container} > ${ARTIFACTS_DIR}/devworkspace-operator/${WEBHOOK_SVR_POD_NAME}/${container}.log + done } # Build latest operator image buildCheOperatorImage() { + #docker build -t "${OPERATOR_IMAGE}" -f Dockerfile . docker build -t "${OPERATOR_IMAGE}" -f Dockerfile . && docker save "${OPERATOR_IMAGE}" > /tmp/operator.tar } copyCheOperatorImageToMinikube() { + #docker save "${OPERATOR_IMAGE}" | minikube ssh --native-ssh=false -- docker load eval $(minikube docker-env) && docker load -i /tmp/operator.tar && rm /tmp/operator.tar } copyCheOperatorImageToMinishift() { + #docker save -o "${OPERATOR_IMAGE}" | minishift ssh "docker load" eval $(minishift docker-env) && docker load -i /tmp/operator.tar && rm /tmp/operator.tar } @@ -508,7 +533,7 @@ waitAllPodsRunning() { fi kubectl get pods -n ${namespace} - sleep 5 + sleep 10 n=$(( n+1 )) done diff --git a/api/v2alpha1/checluster_types.go b/api/v2alpha1/checluster_types.go index 4e52d39e7..353aea260 100644 --- a/api/v2alpha1/checluster_types.go +++ b/api/v2alpha1/checluster_types.go @@ -25,7 +25,7 @@ type CheClusterSpec struct { // Configuration of the workspace endpoints that are exposed on separate domains, as opposed to the subpaths // of the gateway. - WorkspaceDomainEndpoints `json:"workspaceDomainEndpoints,omitempty"` + WorkspaceDomainEndpoints WorkspaceDomainEndpoints `json:"workspaceDomainEndpoints,omitempty"` // Gateway contains the configuration of the gateway used for workspace endpoint routing. Gateway CheGatewaySpec `json:"gateway,omitempty"` diff --git a/bundle/next/eclipse-che-preview-kubernetes/manifests/che-operator.clusterserviceversion.yaml b/bundle/next/eclipse-che-preview-kubernetes/manifests/che-operator.clusterserviceversion.yaml index b6c7bf6fa..128d85e78 100644 --- a/bundle/next/eclipse-che-preview-kubernetes/manifests/che-operator.clusterserviceversion.yaml +++ b/bundle/next/eclipse-che-preview-kubernetes/manifests/che-operator.clusterserviceversion.yaml @@ -83,7 +83,7 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/eclipse-che/che-operator support: Eclipse Foundation - name: eclipse-che-preview-kubernetes.v7.37.0-308.next + name: eclipse-che-preview-kubernetes.v7.37.0-309.next namespace: placeholder spec: apiservicedefinitions: {} @@ -339,6 +339,7 @@ spec: - list - create - update + - watch - apiGroups: - "" resources: @@ -870,7 +871,7 @@ spec: - name: RELATED_IMAGE_che_tls_secrets_creation_job value: quay.io/eclipse/che-tls-secret-creator:alpine-d1ed4ad - name: RELATED_IMAGE_pvc_jobs - value: registry.access.redhat.com/ubi8-minimal:8.4-208 + value: registry.access.redhat.com/ubi8-minimal:8.4-210 - name: RELATED_IMAGE_postgres value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 - name: RELATED_IMAGE_keycloak @@ -1174,4 +1175,4 @@ spec: maturity: stable provider: name: Eclipse Foundation - version: 7.37.0-308.next + version: 7.37.0-309.next diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml b/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml index e06853dbb..1f1b81451 100644 --- a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml +++ b/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml @@ -76,7 +76,7 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/eclipse-che/che-operator support: Eclipse Foundation - name: eclipse-che-preview-openshift.v7.37.0-315.next + name: eclipse-che-preview-openshift.v7.37.0-316.next namespace: placeholder spec: apiservicedefinitions: {} @@ -344,6 +344,7 @@ spec: verbs: - get - list + - watch - apiGroups: - "" resources: @@ -353,6 +354,7 @@ spec: - list - create - update + - watch - apiGroups: - "" resources: @@ -872,7 +874,7 @@ spec: - name: RELATED_IMAGE_devfile_registry value: quay.io/eclipse/che-devfile-registry:next - name: RELATED_IMAGE_pvc_jobs - value: registry.access.redhat.com/ubi8-minimal:8.4-208 + value: registry.access.redhat.com/ubi8-minimal:8.4-210 - name: RELATED_IMAGE_postgres value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 - name: RELATED_IMAGE_keycloak @@ -1198,4 +1200,4 @@ spec: maturity: stable provider: name: Eclipse Foundation - version: 7.37.0-315.next + version: 7.37.0-316.next diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index c607a09e6..5227b2cb6 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -68,7 +68,7 @@ spec: - name: RELATED_IMAGE_che_tls_secrets_creation_job value: quay.io/eclipse/che-tls-secret-creator:alpine-d1ed4ad - name: RELATED_IMAGE_pvc_jobs - value: registry.access.redhat.com/ubi8-minimal:8.4-208 + value: registry.access.redhat.com/ubi8-minimal:8.4-210 - name: RELATED_IMAGE_postgres value: quay.io/eclipse/che--centos--postgresql-96-centos7:9.6-b681d78125361519180a6ac05242c296f8906c11eab7e207b5ca9a89b6344392 - name: RELATED_IMAGE_keycloak diff --git a/config/rbac/cluster_role.yaml b/config/rbac/cluster_role.yaml index 665e10669..690c31e65 100644 --- a/config/rbac/cluster_role.yaml +++ b/config/rbac/cluster_role.yaml @@ -143,6 +143,7 @@ rules: verbs: - get - list + - watch - apiGroups: - "" resources: @@ -152,6 +153,7 @@ rules: - list - create - update + - watch - apiGroups: - '' resources: diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go index 7078e836e..8e8aadb94 100644 --- a/controllers/che/checluster_controller.go +++ b/controllers/che/checluster_controller.go @@ -370,7 +370,7 @@ func (r *CheClusterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) } // Read proxy configuration - proxy, err := r.getProxyConfiguration(deployContext) + proxy, err := GetProxyConfiguration(deployContext) if err != nil { r.Log.Error(err, "Error on reading proxy configuration") return ctrl.Result{}, err diff --git a/controllers/che/proxy.go b/controllers/che/proxy.go index b64677638..96e3a51bf 100644 --- a/controllers/che/proxy.go +++ b/controllers/che/proxy.go @@ -17,7 +17,7 @@ import ( configv1 "github.com/openshift/api/config/v1" ) -func (r *CheClusterReconciler) getProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy, error) { +func GetProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy, error) { // OpenShift 4.x if util.IsOpenShift4 { clusterProxy := &configv1.Proxy{} diff --git a/controllers/che/proxy_test.go b/controllers/che/proxy_test.go index 9c420d298..907d733ec 100644 --- a/controllers/che/proxy_test.go +++ b/controllers/che/proxy_test.go @@ -318,7 +318,6 @@ func TestReadProxyConfiguration(t *testing.T) { scheme.AddKnownTypes(configv1.SchemeGroupVersion, &configv1.Proxy{}) cli := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...) - nonCachedClient := fake.NewFakeClientWithScheme(scheme, testCase.initObjects...) clientSet := fakeclientset.NewSimpleClientset() fakeDiscovery, _ := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery) fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{} @@ -326,13 +325,6 @@ func TestReadProxyConfiguration(t *testing.T) { os.Setenv("OPENSHIFT_VERSION", testCase.openShiftVersion) util.IsOpenShift, util.IsOpenShift4, _ = util.DetectOpenShift() - r := &CheClusterReconciler{ - client: cli, - nonCachedClient: nonCachedClient, - discoveryClient: fakeDiscovery, - Scheme: scheme, - } - deployContext := &deploy.DeployContext{ CheCluster: testCase.cheCluster, ClusterAPI: deploy.ClusterAPI{ @@ -342,7 +334,7 @@ func TestReadProxyConfiguration(t *testing.T) { }, } - actualProxyConf, err := r.getProxyConfiguration(deployContext) + actualProxyConf, err := GetProxyConfiguration(deployContext) if err != nil { t.Fatalf("Error reading proxy configuration: %v", err) } diff --git a/controllers/devworkspace/controller.go b/controllers/devworkspace/controller.go index f8ff85326..02ff799f0 100644 --- a/controllers/devworkspace/controller.go +++ b/controllers/devworkspace/controller.go @@ -87,6 +87,15 @@ func GetCurrentCheClusterInstances() map[client.ObjectKey]v2alpha1.CheCluster { return ret } +// CleanCheClusterInstancesForTest is a helper function for test code in other packages that needs +// to re-initialize the state of the checluster instance cache. +func CleanCheClusterInstancesForTest() { + cheInstancesAccess.Lock() + defer cheInstancesAccess.Unlock() + + currentCheInstances = map[client.ObjectKey]v2alpha1.CheCluster{} +} + // New returns a new instance of the Che manager reconciler. This is mainly useful for // testing because it doesn't set up any watches in the cluster, etc. For that use SetupWithManager. func New(cl client.Client, scheme *runtime.Scheme) CheClusterReconciler { @@ -148,13 +157,11 @@ func (r *CheClusterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) return ctrl.Result{}, r.finalize(ctx, current, currentV1) } - var disabledMessage string - - if !r.scheme.IsGroupRegistered("controller.devfile.io") { + disabledMessage := "" + switch GetDevworkspaceState(r.scheme, current) { + case DevworkspaceStateNotPresent: disabledMessage = "Devworkspace CRDs are not installed" - } - - if disabledMessage == "" && !current.Spec.IsEnabled() { + case DevworkspaceStateDisabled: disabledMessage = "Devworkspace Che is disabled" } diff --git a/controllers/devworkspace/solver/endpoint_exposer.go b/controllers/devworkspace/solver/endpoint_exposer.go index 5ba8c5b79..56f3161b1 100644 --- a/controllers/devworkspace/solver/endpoint_exposer.go +++ b/controllers/devworkspace/solver/endpoint_exposer.go @@ -67,7 +67,7 @@ func (e *RouteExposer) initFrom(ctx context.Context, cl client.Client, cluster * if cluster.Spec.WorkspaceDomainEndpoints.TlsSecretName != "" { secret := &corev1.Secret{} - err := cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.TlsSecretName, Namespace: cluster.Namespace}, secret) + err := cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.WorkspaceDomainEndpoints.TlsSecretName, Namespace: cluster.Namespace}, secret) if err != nil { return err } @@ -94,7 +94,7 @@ func (e *IngressExposer) initFrom(ctx context.Context, cl client.Client, cluster err := cl.Get(ctx, client.ObjectKey{Name: tlsSecretName, Namespace: routing.Namespace}, secret) if errors.IsNotFound(err) { secret = &corev1.Secret{} - err = cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.TlsSecretName, Namespace: cluster.Namespace}, secret) + err = cl.Get(ctx, client.ObjectKey{Name: cluster.Spec.WorkspaceDomainEndpoints.TlsSecretName, Namespace: cluster.Namespace}, secret) if err != nil { return err } diff --git a/controllers/devworkspace/util.go b/controllers/devworkspace/util.go new file mode 100644 index 000000000..0515c27da --- /dev/null +++ b/controllers/devworkspace/util.go @@ -0,0 +1,38 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package devworkspace + +import ( + chev2alpha1 "github.com/eclipse-che/che-operator/api/v2alpha1" + "k8s.io/apimachinery/pkg/runtime" +) + +type DevworkspaceState int + +const ( + DevworkspaceStateNotPresent DevworkspaceState = 0 + DevworkspaceStateDisabled DevworkspaceState = 1 + DevworkspaceStateEnabled DevworkspaceState = 2 +) + +func GetDevworkspaceState(scheme *runtime.Scheme, cr *chev2alpha1.CheCluster) DevworkspaceState { + if !scheme.IsGroupRegistered("controller.devfile.io") { + return DevworkspaceStateNotPresent + } + + if !cr.Spec.IsEnabled() { + return DevworkspaceStateDisabled + } + + return DevworkspaceStateEnabled +} diff --git a/controllers/usernamespace/controller.go b/controllers/usernamespace/controller.go new file mode 100644 index 000000000..43a6d8173 --- /dev/null +++ b/controllers/usernamespace/controller.go @@ -0,0 +1,404 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package usernamespace + +import ( + "context" + + "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + org "github.com/eclipse-che/che-operator/api" + v1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/eclipse-che/che-operator/api/v2alpha1" + "github.com/eclipse-che/che-operator/controllers/che" + "github.com/eclipse-che/che-operator/controllers/devworkspace" + "github.com/eclipse-che/che-operator/controllers/devworkspace/defaults" + "github.com/eclipse-che/che-operator/pkg/deploy" + projectv1 "github.com/openshift/api/project/v1" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +const ( + userSettingsComponentLabelValue = "user-settings" +) + +type CheUserNamespaceReconciler struct { + client client.Client + scheme *runtime.Scheme + namespaceCache namespaceCache +} + +var _ reconcile.Reconciler = (*CheUserNamespaceReconciler)(nil) + +func NewReconciler() *CheUserNamespaceReconciler { + return &CheUserNamespaceReconciler{namespaceCache: *NewNamespaceCache()} +} + +func (r *CheUserNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.scheme = mgr.GetScheme() + r.client = mgr.GetClient() + r.namespaceCache.client = r.client + + var obj runtime.Object + if infrastructure.IsOpenShift() { + obj = &projectv1.Project{} + } else { + obj = &corev1.Namespace{} + } + + ctx := context.Background() + bld := ctrl.NewControllerManagedBy(mgr). + For(obj). + Watches(&source.Kind{Type: &corev1.Secret{}}, r.watchRulesForSecrets(ctx)). + Watches(&source.Kind{Type: &corev1.ConfigMap{}}, r.watchRulesForConfigMaps(ctx)). + Watches(&source.Kind{Type: &v1.CheCluster{}}, r.triggerAllNamespaces(ctx)) + + return bld.Complete(r) +} + +func (r *CheUserNamespaceReconciler) watchRulesForSecrets(ctx context.Context) handler.EventHandler { + return &handler.EnqueueRequestsFromMapFunc{ + ToRequests: handler.ToRequestsFunc(func(mo handler.MapObject) []reconcile.Request { + return asReconcileRequestForNamespaceIf(mo.Meta, func() bool { + return (isLabeledAsUserSettings(mo.Meta) && r.isInManagedNamespace(ctx, mo.Meta)) || + r.hasNameAndIsCollocatedWithCheCluster(ctx, mo.Meta, deploy.CheTLSSelfSignedCertificateSecretName) + }) + }), + } +} + +func (r *CheUserNamespaceReconciler) watchRulesForConfigMaps(ctx context.Context) handler.EventHandler { + return &handler.EnqueueRequestsFromMapFunc{ + ToRequests: handler.ToRequestsFunc(func(mo handler.MapObject) []reconcile.Request { + return asReconcileRequestForNamespaceIf(mo.Meta, func() bool { + return (isLabeledAsUserSettings(mo.Meta) && r.isInManagedNamespace(ctx, mo.Meta)) || + r.hasNameAndIsCollocatedWithCheCluster(ctx, mo.Meta, deploy.CheAllCACertsConfigMapName) + }) + }), + } +} + +func (r *CheUserNamespaceReconciler) hasNameAndIsCollocatedWithCheCluster(ctx context.Context, obj metav1.Object, names ...string) bool { + for _, n := range names { + if obj.GetName() == n && r.hasCheCluster(ctx, obj.GetNamespace()) { + return true + } + } + + return false +} + +func isLabeledAsUserSettings(obj metav1.Object) bool { + return obj.GetLabels()["app.kubernetes.io/component"] == userSettingsComponentLabelValue +} + +func (r *CheUserNamespaceReconciler) isInManagedNamespace(ctx context.Context, obj metav1.Object) bool { + info, err := r.namespaceCache.GetNamespaceInfo(ctx, obj.GetNamespace()) + return err == nil && info != nil && info.OwnerUid != "" +} + +func (r *CheUserNamespaceReconciler) triggerAllNamespaces(ctx context.Context) handler.EventHandler { + return &handler.EnqueueRequestsFromMapFunc{ + ToRequests: handler.ToRequestsFunc(func(mo handler.MapObject) []reconcile.Request { + nss := r.namespaceCache.GetAllKnownNamespaces() + ret := make([]reconcile.Request, len(nss)) + + for _, ns := range nss { + ret = append(ret, reconcile.Request{ + NamespacedName: types.NamespacedName{Name: ns}, + }) + } + + return ret + }), + } +} + +func (r *CheUserNamespaceReconciler) hasCheCluster(ctx context.Context, namespace string) bool { + list := v1.CheClusterList{} + if err := r.client.List(ctx, &list, client.InNamespace(namespace)); err != nil { + return false + } + + return len(list.Items) > 0 +} + +func asReconcileRequestForNamespace(obj metav1.Object) []reconcile.Request { + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{Name: obj.GetNamespace()}, + }, + } +} + +func asReconcileRequestForNamespaceIf(obj metav1.Object, predicate func() bool) []reconcile.Request { + if predicate() { + return asReconcileRequestForNamespace(obj) + } else { + return []reconcile.Request{} + } +} + +func (r *CheUserNamespaceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + ctx := context.Background() + + info, err := r.namespaceCache.ExamineNamespace(ctx, req.Name) + if err != nil { + logrus.Errorf("Failed to examine namespace %s for presence of Che user info labels: %v", req.Name, err) + return ctrl.Result{}, err + } + + if info == nil || info.OwnerUid == "" { + // we're not handling this namespace + return ctrl.Result{}, nil + } + + checluster := findManagingCheCluster(*info.CheCluster) + if checluster == nil { + return ctrl.Result{Requeue: true}, nil + } + + if devworkspace.GetDevworkspaceState(r.scheme, checluster) != devworkspace.DevworkspaceStateEnabled { + return ctrl.Result{}, nil + } + + // let's construct the deployContext to be able to use methods from v1 operator + deployContext := &deploy.DeployContext{ + CheCluster: org.AsV1(checluster), + ClusterAPI: deploy.ClusterAPI{ + Client: r.client, + NonCachedClient: r.client, + DiscoveryClient: nil, + Scheme: r.scheme, + }, + } + + if err = r.reconcileSelfSignedCert(ctx, deployContext, req.Name, checluster); err != nil { + logrus.Errorf("Failed to reconcile self-signed certificate into namespace '%s': %v", req.Name, err) + return ctrl.Result{}, err + } + + if err = r.reconcileTrustedCerts(ctx, deployContext, req.Name, checluster); err != nil { + logrus.Errorf("Failed to reconcile self-signed certificate into namespace '%s': %v", req.Name, err) + return ctrl.Result{}, err + } + + if err = r.reconcileProxySettings(ctx, req.Name, checluster, deployContext); err != nil { + logrus.Errorf("Failed to reconcile proxy settings into namespace '%s': %v", req.Name, err) + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +func findManagingCheCluster(key types.NamespacedName) *v2alpha1.CheCluster { + instances := devworkspace.GetCurrentCheClusterInstances() + if len(instances) == 0 { + return nil + } + + if len(instances) == 1 { + for k, v := range instances { + if key.Name == "" || (key.Name == k.Name && key.Namespace == k.Namespace) { + return &v + } + return nil + } + } + + ret, ok := instances[key] + + if ok { + return &ret + } else { + return nil + } +} + +func (r *CheUserNamespaceReconciler) reconcileSelfSignedCert(ctx context.Context, deployContext *deploy.DeployContext, targetNs string, checluster *v2alpha1.CheCluster) error { + targetCertName := prefixedName(checluster, "server-cert") + + delSecret := func() error { + _, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetCertName, Namespace: targetNs}, &corev1.Secret{}) + return err + } + + cheCert := &corev1.Secret{} + if err := r.client.Get(ctx, client.ObjectKey{Name: deploy.CheTLSSelfSignedCertificateSecretName, Namespace: checluster.Namespace}, cheCert); err != nil { + if !errors.IsNotFound(err) { + return err + } + // There is not self-signed cert in the namespace of the checluster, so we have nothing to copy around + return delSecret() + } + + if _, ok := cheCert.Data["ca.crt"]; !ok { + // the secret doesn't contain the certificate. bail out. + return delSecret() + } + + targetCert := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: targetCertName, + Namespace: targetNs, + Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ + constants.DevWorkspaceMountLabel: "true", + }), + Annotations: map[string]string{ + constants.DevWorkspaceMountAsAnnotation: "file", + constants.DevWorkspaceMountPathAnnotation: "/tmp/che/secret/", + }, + }, + Data: map[string][]byte{ + "ca.crt": cheCert.Data["ca.crt"], + }, + } + + _, err := deploy.DoSync(deployContext, targetCert, deploy.SecretDiffOpts) + return err +} + +func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context, deployContext *deploy.DeployContext, targetNs string, checluster *v2alpha1.CheCluster) error { + targetConfigMapName := prefixedName(checluster, "trusted-ca-certs") + + delConfigMap := func() error { + _, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetConfigMapName, Namespace: targetNs}, &corev1.Secret{}) + return err + } + + sourceMap := &corev1.ConfigMap{} + if err := r.client.Get(ctx, client.ObjectKey{Name: deploy.CheAllCACertsConfigMapName, Namespace: checluster.Namespace}, sourceMap); err != nil { + if !errors.IsNotFound(err) { + return err + } + + return delConfigMap() + } + + targetMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: targetConfigMapName, + Namespace: targetNs, + Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ + constants.DevWorkspaceMountLabel: "true", + }), + Annotations: addToFirst(sourceMap.Annotations, map[string]string{ + constants.DevWorkspaceMountAsAnnotation: "file", + constants.DevWorkspaceMountPathAnnotation: "/public-certs", + }), + }, + Data: sourceMap.Data, + } + + _, err := deploy.DoSync(deployContext, targetMap, deploy.ConfigMapDiffOpts) + return err +} + +func addToFirst(first map[string]string, second map[string]string) map[string]string { + if first == nil { + first = map[string]string{} + } + for k, v := range second { + first[k] = v + } + + return first +} + +func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, targetNs string, checluster *v2alpha1.CheCluster, deployContext *deploy.DeployContext) error { + proxyConfig, err := che.GetProxyConfiguration(deployContext) + if err != nil { + return err + } + + if proxyConfig == nil { + return nil + } + + proxySettings := map[string]string{} + if proxyConfig.HttpProxy != "" { + proxySettings["HTTP_PROXY"] = proxyConfig.HttpProxy + } + if proxyConfig.HttpsProxy != "" { + proxySettings["HTTPS_PROXY"] = proxyConfig.HttpsProxy + } + if proxyConfig.NoProxy != "" { + proxySettings["NO_PROXY"] = proxyConfig.NoProxy + } + + key := client.ObjectKey{Name: prefixedName(checluster, "proxy-settings"), Namespace: targetNs} + cfg := &corev1.ConfigMap{} + exists := true + if err := r.client.Get(ctx, key, cfg); err != nil { + if errors.IsNotFound(err) { + exists = false + } else { + return err + } + } + + if len(proxySettings) == 0 { + if exists { + if err := r.client.Delete(ctx, cfg); err != nil { + return err + } + } + return nil + } + + requiredLabels := defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{ + constants.DevWorkspaceMountLabel: "true", + }) + requiredAnnos := map[string]string{ + constants.DevWorkspaceMountAsAnnotation: "env", + } + + cfg = &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: prefixedName(checluster, "proxy-settings"), + Namespace: targetNs, + Labels: requiredLabels, + Annotations: requiredAnnos, + }, + Data: proxySettings, + } + + _, err = deploy.DoSync(deployContext, cfg, deploy.ConfigMapDiffOpts) + return err +} + +func prefixedName(checluster *v2alpha1.CheCluster, name string) string { + return checluster.Name + "-" + checluster.Namespace + "-" + name +} diff --git a/controllers/usernamespace/controller_test.go b/controllers/usernamespace/controller_test.go new file mode 100644 index 000000000..3021d341c --- /dev/null +++ b/controllers/usernamespace/controller_test.go @@ -0,0 +1,330 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package usernamespace + +import ( + "context" + "sync" + "testing" + + "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + v1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/eclipse-che/che-operator/controllers/devworkspace" + "github.com/eclipse-che/che-operator/pkg/deploy" + "github.com/eclipse-che/che-operator/pkg/util" + configv1 "github.com/openshift/api/config/v1" + projectv1 "github.com/openshift/api/project/v1" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme *runtime.Scheme, cheNamespaceName string, cheName string) { + var cheNamespace metav1.Object + if infrastructure.IsOpenShift() { + cheNamespace = &projectv1.Project{} + } else { + cheNamespace = &corev1.Namespace{} + } + + cheNamespace.SetName(cheNamespaceName) + if err := cl.Create(ctx, cheNamespace.(runtime.Object)); err != nil { + t.Fatal(err) + } + + cheCluster := v1.CheCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: cheName, + Namespace: cheNamespaceName, + }, + Spec: v1.CheClusterSpec{ + Server: v1.CheClusterSpecServer{ + CheHost: "che-host", + CustomCheProperties: map[string]string{ + "CHE_INFRA_OPENSHIFT_ROUTE_HOST_DOMAIN__SUFFIX": "root-domain", + }, + }, + DevWorkspace: v1.CheClusterSpecDevWorkspace{ + Enable: true, + }, + K8s: v1.CheClusterSpecK8SOnly{ + IngressDomain: "root-domain", + }, + }, + } + if err := cl.Create(ctx, &cheCluster); err != nil { + t.Fatal(err) + } + + // also create the self-signed-certificate secret to pretend we have TLS set up + cert := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploy.CheTLSSelfSignedCertificateSecretName, + Namespace: cheNamespaceName, + }, + Data: map[string][]byte{ + "ca.crt": []byte("my certificate"), + "other.data": []byte("should not be copied to target ns"), + }, + } + if err := cl.Create(ctx, cert); err != nil { + t.Fatal(err) + } + + caCerts := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploy.CheAllCACertsConfigMapName, + Namespace: cheNamespaceName, + }, + Data: map[string]string{ + "trusted1": "trusted cert 1", + "trusted2": "trusted cert 2", + }, + } + if err := cl.Create(ctx, caCerts); err != nil { + t.Fatal(err) + } + + r := devworkspace.New(cl, scheme) + // the reconciliation needs to run twice for it to be truly finished - we're setting up finalizers etc... + if _, err := r.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: cheName, Namespace: cheNamespaceName}}); err != nil { + t.Fatal(err) + } + if _, err := r.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: cheName, Namespace: cheNamespaceName}}); err != nil { + t.Fatal(err) + } +} + +func setup(infraType infrastructure.Type, objs ...runtime.Object) (*runtime.Scheme, client.Client, *CheUserNamespaceReconciler) { + infrastructure.InitializeForTesting(infraType) + devworkspace.CleanCheClusterInstancesForTest() + util.IsOpenShift = infraType == infrastructure.OpenShiftv4 + util.IsOpenShift4 = infraType == infrastructure.OpenShiftv4 + + scheme := createTestScheme() + + cl := fake.NewFakeClientWithScheme(scheme, objs...) + + r := &CheUserNamespaceReconciler{ + client: cl, + scheme: scheme, + namespaceCache: namespaceCache{ + client: cl, + knownNamespaces: map[string]namespaceInfo{}, + lock: sync.Mutex{}, + }, + } + + return scheme, cl, r +} + +func TestSkipsUnlabeledNamespaces(t *testing.T) { + test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object) { + ctx := context.TODO() + scheme, cl, r := setup(infraType, namespace.(runtime.Object)) + setupCheCluster(t, ctx, cl, scheme, "che", "che") + + if _, err := r.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: namespace.GetName()}}); err != nil { + t.Fatal(err) + } + + // no new secret or configmap should be created in the namespace + ss := &corev1.SecretList{} + if err := cl.List(ctx, ss, client.InNamespace(namespace.GetName())); err != nil { + t.Fatal(err) + } + + assert.True(t, len(ss.Items) == 0, "No secrets expected in the tested namespace but found %d", len(ss.Items)) + + cs := &corev1.ConfigMapList{} + if err := cl.List(ctx, cs, client.InNamespace(namespace.GetName())); err != nil { + t.Fatal(err) + } + assert.True(t, len(cs.Items) == 0, "No configmaps expected in the tested namespace but found %d", len(cs.Items)) + } + + t.Run("k8s", func(t *testing.T) { + test(t, infrastructure.Kubernetes, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + }, + }) + }) + + t.Run("openshift", func(t *testing.T) { + test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prj", + }, + }) + }) +} + +func TestRequiresLabelsToMatchOneOfMultipleCheCluster(t *testing.T) { + test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object) { + ctx := context.TODO() + scheme, cl, r := setup(infraType, namespace.(runtime.Object)) + setupCheCluster(t, ctx, cl, scheme, "che1", "che") + setupCheCluster(t, ctx, cl, scheme, "che2", "che") + + res, err := r.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: namespace.GetName()}}) + assert.NoError(t, err, "Reconciliation should have succeeded.") + + assert.True(t, res.Requeue, "The reconciliation request should have been requeued.") + } + + t.Run("k8s", func(t *testing.T) { + test(t, infrastructure.Kubernetes, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + Labels: map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + }, + }, + }) + }) + + t.Run("openshift", func(t *testing.T) { + test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prj", + Labels: map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + }, + }, + }) + }) +} + +func TestMatchingCheClusterCanBeSelectedUsingLabels(t *testing.T) { + test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object) { + ctx := context.TODO() + scheme, cl, r := setup(infraType, namespace.(runtime.Object)) + setupCheCluster(t, ctx, cl, scheme, "che1", "che") + setupCheCluster(t, ctx, cl, scheme, "che2", "che") + + res, err := r.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: namespace.GetName()}}) + assert.NoError(t, err, "Reconciliation shouldn't have failed") + + assert.False(t, res.Requeue, "The reconciliation request should have succeeded but is requesting a requeue.") + } + + t.Run("k8s", func(t *testing.T) { + test(t, infrastructure.Kubernetes, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + Labels: map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + cheNameLabel: "che", + cheNamespaceLabel: "che1", + }, + }, + }) + }) + + t.Run("openshift", func(t *testing.T) { + test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prj", + Labels: map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + cheNameLabel: "che", + cheNamespaceLabel: "che1", + }, + }, + }) + }) +} + +func TestCreatesDataInNamespace(t *testing.T) { + test := func(t *testing.T, infraType infrastructure.Type, namespace metav1.Object, objs ...runtime.Object) { + ctx := context.TODO() + allObjs := append(objs, namespace.(runtime.Object)) + scheme, cl, r := setup(infraType, allObjs...) + setupCheCluster(t, ctx, cl, scheme, "eclipse-che", "che") + + res, err := r.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Name: namespace.GetName()}}) + assert.NoError(t, err, "Reconciliation should have succeeded") + + assert.False(t, res.Requeue, "The reconciliation request should have succeeded but it is requesting a requeue") + + proxySettings := corev1.ConfigMap{} + assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-eclipse-che-proxy-settings", Namespace: namespace.GetName()}, &proxySettings)) + + assert.Equal(t, "env", proxySettings.GetAnnotations()[constants.DevWorkspaceMountAsAnnotation], + "proxy settings should be annotated as mount as 'env'") + + assert.Equal(t, "true", proxySettings.GetLabels()[constants.DevWorkspaceMountLabel], + "proxy settings should be labeled as mounted") + + assert.Equal(t, 1, len(proxySettings.Data), "Expecting just 1 element in the default proxy settings") + + assert.Equal(t, ".svc", proxySettings.Data["NO_PROXY"], "Unexpected proxy settings") + + cert := corev1.Secret{} + assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-eclipse-che-server-cert", Namespace: namespace.GetName()}, &cert)) + + assert.Equal(t, "file", cert.GetAnnotations()[constants.DevWorkspaceMountAsAnnotation], "server cert should be annotated as mount as 'file'") + assert.Equal(t, "/tmp/che/secret/", cert.GetAnnotations()[constants.DevWorkspaceMountPathAnnotation], "server cert annotated as mounted to an unexpected path") + assert.Equal(t, "true", cert.GetLabels()[constants.DevWorkspaceMountLabel], "server cert should be labeled as mounted") + assert.Equal(t, 1, len(cert.Data), "Expecting just 1 element in the self-signed cert") + assert.Equal(t, "my certificate", string(cert.Data["ca.crt"]), "Unexpected self-signed certificate") + + caCerts := corev1.ConfigMap{} + assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-eclipse-che-trusted-ca-certs", Namespace: namespace.GetName()}, &caCerts)) + assert.Equal(t, "file", caCerts.GetAnnotations()[constants.DevWorkspaceMountAsAnnotation], "trusted certs should be annotated as mount as 'file'") + assert.Equal(t, "/public-certs", caCerts.GetAnnotations()[constants.DevWorkspaceMountPathAnnotation], "trusted certs annotated as mounted to an unexpected path") + assert.Equal(t, "true", caCerts.GetLabels()[constants.DevWorkspaceMountLabel], "trusted certs should be labeled as mounted") + assert.Equal(t, 2, len(caCerts.Data), "Expecting exactly 2 data entries in the trusted cert config map") + assert.Equal(t, "trusted cert 1", string(caCerts.Data["trusted1"]), "Unexpected trusted cert 1 value") + assert.Equal(t, "trusted cert 2", string(caCerts.Data["trusted2"]), "Unexpected trusted cert 2 value") + } + + t.Run("k8s", func(t *testing.T) { + test(t, infrastructure.Kubernetes, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + Labels: map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + }, + }, + }) + }) + + t.Run("openshift", func(t *testing.T) { + test(t, infrastructure.OpenShiftv4, &projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prj", + Labels: map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + }, + }, + }, &configv1.Proxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1.ProxySpec{ + NoProxy: ".svc", + }, + Status: configv1.ProxyStatus{ + NoProxy: ".svc", + }, + }) + }) +} diff --git a/controllers/usernamespace/namespacecache.go b/controllers/usernamespace/namespacecache.go new file mode 100644 index 000000000..031231bf0 --- /dev/null +++ b/controllers/usernamespace/namespacecache.go @@ -0,0 +1,133 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package usernamespace + +import ( + "context" + "sync" + + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + projectv1 "github.com/openshift/api/project/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + workspaceNamespaceOwnerUidLabel string = "che.eclipse.org/workspace-namespace-owner-uid" + cheNameLabel string = "che.eclipse.org/che-name" + cheNamespaceLabel string = "che.eclipse.org/che-namespace" +) + +type namespaceCache struct { + client client.Client + knownNamespaces map[string]namespaceInfo + lock sync.Mutex +} + +type namespaceInfo struct { + OwnerUid string + CheCluster *types.NamespacedName +} + +func NewNamespaceCache() *namespaceCache { + return &namespaceCache{ + knownNamespaces: map[string]namespaceInfo{}, + lock: sync.Mutex{}, + } +} + +func (c *namespaceCache) GetNamespaceInfo(ctx context.Context, namespace string) (*namespaceInfo, error) { + c.lock.Lock() + defer c.lock.Unlock() + + for { + val, contains := c.knownNamespaces[namespace] + if contains { + return &val, nil + } else { + existing, err := c.examineNamespaceUnsafe(ctx, namespace) + if err != nil { + return nil, err + } else if existing == nil { + return nil, nil + } + } + } +} +func (c *namespaceCache) ExamineNamespace(ctx context.Context, ns string) (*namespaceInfo, error) { + c.lock.Lock() + defer c.lock.Unlock() + + return c.examineNamespaceUnsafe(ctx, ns) +} + +func (c *namespaceCache) GetAllKnownNamespaces() []string { + c.lock.Lock() + defer c.lock.Unlock() + + ret := make([]string, len(c.knownNamespaces)) + for k, _ := range c.knownNamespaces { + ret = append(ret, k) + } + + return ret +} + +func (c *namespaceCache) examineNamespaceUnsafe(ctx context.Context, ns string) (*namespaceInfo, error) { + var obj runtime.Object + if infrastructure.IsOpenShift() { + obj = &projectv1.Project{} + } else { + obj = &corev1.Namespace{} + } + + if err := c.client.Get(ctx, client.ObjectKey{Name: ns}, obj); err != nil { + if errors.IsNotFound(err) { + delete(c.knownNamespaces, ns) + return nil, nil + } + return nil, err + } + + var namespace = obj.(metav1.Object) + + if namespace.GetDeletionTimestamp() != nil { + delete(c.knownNamespaces, ns) + return nil, nil + } + + labels := namespace.GetLabels() + if labels == nil { + labels = map[string]string{} + } + + ownerUid := labels[workspaceNamespaceOwnerUidLabel] + cheName := labels[cheNameLabel] + cheNamespace := labels[cheNamespaceLabel] + + ret := namespaceInfo{ + OwnerUid: ownerUid, + CheCluster: &types.NamespacedName{ + Name: cheName, + Namespace: cheNamespace, + }, + } + + c.knownNamespaces[ns] = ret + + return &ret, nil +} diff --git a/controllers/usernamespace/namespacecache_test.go b/controllers/usernamespace/namespacecache_test.go new file mode 100644 index 000000000..b04d35bc1 --- /dev/null +++ b/controllers/usernamespace/namespacecache_test.go @@ -0,0 +1,136 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package usernamespace + +import ( + "context" + "sync" + "testing" + + dwo "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + v1 "github.com/eclipse-che/che-operator/api/v1" + "github.com/stretchr/testify/assert" + + projectv1 "github.com/openshift/api/project/v1" + routev1 "github.com/openshift/api/route/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + extensions "k8s.io/api/extensions/v1beta1" + "k8s.io/api/node/v1alpha1" + rbac "k8s.io/api/rbac/v1" + + configv1 "github.com/openshift/api/config/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func createTestScheme() *runtime.Scheme { + scheme := runtime.NewScheme() + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(extensions.AddToScheme(scheme)) + utilruntime.Must(corev1.AddToScheme(scheme)) + utilruntime.Must(appsv1.AddToScheme(scheme)) + utilruntime.Must(rbac.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(v1.AddToScheme(scheme)) + utilruntime.Must(dwo.AddToScheme(scheme)) + utilruntime.Must(projectv1.AddToScheme(scheme)) + utilruntime.Must(configv1.AddToScheme(scheme)) + + return scheme +} + +func TestGetNamespaceInfoReadsFromCache(t *testing.T) { + test := func(infraType infrastructure.Type, namespace metav1.Object) { + infrastructure.InitializeForTesting(infraType) + ctx := context.TODO() + + ns := namespace.GetName() + cl := fake.NewFakeClientWithScheme(createTestScheme(), namespace.(runtime.Object)) + + nsc := namespaceCache{ + client: cl, + knownNamespaces: map[string]namespaceInfo{}, + lock: sync.Mutex{}, + } + + _, err := nsc.GetNamespaceInfo(ctx, ns) + assert.NoError(t, err) + assert.Contains(t, nsc.knownNamespaces, ns, "The namespace info should have been cached") + } + + test(infrastructure.Kubernetes, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + }, + }) + + test(infrastructure.OpenShiftv4, &projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prj", + }, + }) +} + +func TestExamineUpdatesCache(t *testing.T) { + test := func(infraType infrastructure.Type, namespace metav1.Object) { + ctx := context.TODO() + + nsName := namespace.GetName() + cl := fake.NewFakeClientWithScheme(createTestScheme(), namespace.(runtime.Object)) + infrastructure.InitializeForTesting(infraType) + + nsc := namespaceCache{ + client: cl, + knownNamespaces: map[string]namespaceInfo{}, + lock: sync.Mutex{}, + } + + nsi, err := nsc.GetNamespaceInfo(ctx, nsName) + assert.NoError(t, err) + + assert.Empty(t, nsi.OwnerUid, "Detected owner UID should be empty") + + assert.Contains(t, nsc.knownNamespaces, nsName, "The namespace info should have been cached") + + ns := namespace.(runtime.Object).DeepCopyObject() + assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: nsName}, ns)) + + ns.(metav1.Object).SetLabels(map[string]string{ + workspaceNamespaceOwnerUidLabel: "uid", + }) + + assert.NoError(t, cl.Update(ctx, ns)) + + nsi, err = nsc.ExamineNamespace(ctx, nsName) + assert.NoError(t, err) + + assert.Equal(t, "uid", nsi.OwnerUid, "unexpected detected owner UID") + } + + test(infrastructure.Kubernetes, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + }, + }) + + test(infrastructure.OpenShiftv4, &projectv1.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prj", + }, + }) +} diff --git a/main.go b/main.go index cff65242f..fe64aac64 100644 --- a/main.go +++ b/main.go @@ -47,6 +47,7 @@ import ( restorecontroller "github.com/eclipse-che/che-operator/controllers/checlusterrestore" "github.com/eclipse-che/che-operator/controllers/devworkspace" "github.com/eclipse-che/che-operator/controllers/devworkspace/solver" + "github.com/eclipse-che/che-operator/controllers/usernamespace" "github.com/eclipse-che/che-operator/pkg/deploy" "github.com/eclipse-che/che-operator/pkg/signal" "github.com/eclipse-che/che-operator/pkg/util" @@ -62,6 +63,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" image_puller_api "github.com/che-incubator/kubernetes-image-puller-operator/pkg/apis" + projectv1 "github.com/openshift/api/project/v1" routev1 "github.com/openshift/api/route/v1" userv1 "github.com/openshift/api/user/v1" corev1 "k8s.io/api/core/v1" @@ -130,6 +132,7 @@ func init() { utilruntime.Must(configv1.AddToScheme(scheme)) utilruntime.Must(corev1.AddToScheme(scheme)) utilruntime.Must(consolev1.AddToScheme(scheme)) + utilruntime.Must(projectv1.AddToScheme(scheme)) } } @@ -304,8 +307,20 @@ func enableDevworkspaceSupport(mgr manager.Manager) error { SolverGetter: solver.Getter(mgr.GetScheme()), } if err := routing.SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to set up controller", "controller", "DevWorkspaceRouting") return err } + + userNamespaceReconciler := usernamespace.NewReconciler() + + if err = userNamespaceReconciler.SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to set up controller", "controller", "CheUserReconciler") + return err + } + + setupLog.Info("Devworkspace support enabled") + } else { + setupLog.Info("Devworkspace support disabled") } return nil diff --git a/pkg/deploy/configmap.go b/pkg/deploy/configmap.go index d5c397da3..b08acede1 100644 --- a/pkg/deploy/configmap.go +++ b/pkg/deploy/configmap.go @@ -20,7 +20,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var configMapDiffOpts = cmp.Options{ +var ConfigMapDiffOpts = cmp.Options{ cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"), cmp.Comparer(func(x, y metav1.ObjectMeta) bool { return reflect.DeepEqual(x.Labels, y.Labels) @@ -34,14 +34,14 @@ func SyncConfigMapDataToCluster( component string) (bool, error) { configMapSpec := GetConfigMapSpec(deployContext, name, data, component) - return Sync(deployContext, configMapSpec, configMapDiffOpts) + return Sync(deployContext, configMapSpec, ConfigMapDiffOpts) } func SyncConfigMapSpecToCluster( deployContext *DeployContext, configMapSpec *corev1.ConfigMap) (bool, error) { - return Sync(deployContext, configMapSpec, configMapDiffOpts) + return Sync(deployContext, configMapSpec, ConfigMapDiffOpts) } func GetConfigMapSpec( diff --git a/pkg/deploy/secret.go b/pkg/deploy/secret.go index 56d923d53..bf3d4e8b8 100644 --- a/pkg/deploy/secret.go +++ b/pkg/deploy/secret.go @@ -27,7 +27,7 @@ import ( "k8s.io/apimachinery/pkg/types" ) -var secretDiffOpts = cmp.Options{ +var SecretDiffOpts = cmp.Options{ cmpopts.IgnoreFields(corev1.Secret{}, "TypeMeta", "ObjectMeta"), } @@ -39,7 +39,7 @@ func SyncSecretToCluster( data map[string][]byte) (bool, error) { secretSpec := GetSecretSpec(deployContext, name, namespace, data) - return Sync(deployContext, secretSpec, secretDiffOpts) + return Sync(deployContext, secretSpec, SecretDiffOpts) } // Get all secrets by labels and annotations diff --git a/pkg/deploy/sync.go b/pkg/deploy/sync.go index f9d434f53..ad2e6fc4a 100644 --- a/pkg/deploy/sync.go +++ b/pkg/deploy/sync.go @@ -28,6 +28,10 @@ import ( // Sync syncs the blueprint to the cluster in a generic (as much as Go allows) manner. // Returns true if object is up to date otherwiser returns false +// +// WARNING: For legacy reasons, this method bails out quickly without doing anything if the CheCluster resource +// is being deleted (it does this by examining the deployContext, not the cluster). If you don't want +// this behavior, use the DoSync method. func Sync(deployContext *DeployContext, blueprint metav1.Object, diffOpts ...cmp.Option) (bool, error) { // eclipse-che custom resource is being deleted, we shouldn't sync // TODO move this check before `Sync` invocation @@ -35,6 +39,12 @@ func Sync(deployContext *DeployContext, blueprint metav1.Object, diffOpts ...cmp return true, nil } + return DoSync(deployContext, blueprint, diffOpts...) +} + +// Sync syncs the blueprint to the cluster in a generic (as much as Go allows) manner. +// Returns true if object is up to date otherwiser returns false +func DoSync(deployContext *DeployContext, blueprint metav1.Object, diffOpts ...cmp.Option) (bool, error) { runtimeObject, ok := blueprint.(runtime.Object) if !ok { return false, fmt.Errorf("object %T is not a runtime.Object. Cannot sync it", runtimeObject) diff --git a/vendor/github.com/openshift/api/project/v1/doc.go b/vendor/github.com/openshift/api/project/v1/doc.go new file mode 100644 index 000000000..5bbd9d5ea --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/doc.go @@ -0,0 +1,8 @@ +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/openshift/origin/pkg/project/apis/project +// +k8s:defaulter-gen=TypeMeta +// +k8s:openapi-gen=true + +// +groupName=project.openshift.io +// Package v1 is the v1 version of the API. +package v1 diff --git a/vendor/github.com/openshift/api/project/v1/generated.pb.go b/vendor/github.com/openshift/api/project/v1/generated.pb.go new file mode 100644 index 000000000..f6f1737b2 --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/generated.pb.go @@ -0,0 +1,1346 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/openshift/api/project/v1/generated.proto + +package v1 + +import ( + fmt "fmt" + + io "io" + + proto "github.com/gogo/protobuf/proto" + k8s_io_api_core_v1 "k8s.io/api/core/v1" + v11 "k8s.io/api/core/v1" + + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +func (m *Project) Reset() { *m = Project{} } +func (*Project) ProtoMessage() {} +func (*Project) Descriptor() ([]byte, []int) { + return fileDescriptor_fbf46eaac05029bf, []int{0} +} +func (m *Project) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Project) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *Project) XXX_Merge(src proto.Message) { + xxx_messageInfo_Project.Merge(m, src) +} +func (m *Project) XXX_Size() int { + return m.Size() +} +func (m *Project) XXX_DiscardUnknown() { + xxx_messageInfo_Project.DiscardUnknown(m) +} + +var xxx_messageInfo_Project proto.InternalMessageInfo + +func (m *ProjectList) Reset() { *m = ProjectList{} } +func (*ProjectList) ProtoMessage() {} +func (*ProjectList) Descriptor() ([]byte, []int) { + return fileDescriptor_fbf46eaac05029bf, []int{1} +} +func (m *ProjectList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProjectList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ProjectList) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProjectList.Merge(m, src) +} +func (m *ProjectList) XXX_Size() int { + return m.Size() +} +func (m *ProjectList) XXX_DiscardUnknown() { + xxx_messageInfo_ProjectList.DiscardUnknown(m) +} + +var xxx_messageInfo_ProjectList proto.InternalMessageInfo + +func (m *ProjectRequest) Reset() { *m = ProjectRequest{} } +func (*ProjectRequest) ProtoMessage() {} +func (*ProjectRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fbf46eaac05029bf, []int{2} +} +func (m *ProjectRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProjectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ProjectRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProjectRequest.Merge(m, src) +} +func (m *ProjectRequest) XXX_Size() int { + return m.Size() +} +func (m *ProjectRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ProjectRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ProjectRequest proto.InternalMessageInfo + +func (m *ProjectSpec) Reset() { *m = ProjectSpec{} } +func (*ProjectSpec) ProtoMessage() {} +func (*ProjectSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_fbf46eaac05029bf, []int{3} +} +func (m *ProjectSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProjectSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ProjectSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProjectSpec.Merge(m, src) +} +func (m *ProjectSpec) XXX_Size() int { + return m.Size() +} +func (m *ProjectSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ProjectSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_ProjectSpec proto.InternalMessageInfo + +func (m *ProjectStatus) Reset() { *m = ProjectStatus{} } +func (*ProjectStatus) ProtoMessage() {} +func (*ProjectStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_fbf46eaac05029bf, []int{4} +} +func (m *ProjectStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProjectStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ProjectStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProjectStatus.Merge(m, src) +} +func (m *ProjectStatus) XXX_Size() int { + return m.Size() +} +func (m *ProjectStatus) XXX_DiscardUnknown() { + xxx_messageInfo_ProjectStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_ProjectStatus proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Project)(nil), "github.com.openshift.api.project.v1.Project") + proto.RegisterType((*ProjectList)(nil), "github.com.openshift.api.project.v1.ProjectList") + proto.RegisterType((*ProjectRequest)(nil), "github.com.openshift.api.project.v1.ProjectRequest") + proto.RegisterType((*ProjectSpec)(nil), "github.com.openshift.api.project.v1.ProjectSpec") + proto.RegisterType((*ProjectStatus)(nil), "github.com.openshift.api.project.v1.ProjectStatus") +} + +func init() { + proto.RegisterFile("github.com/openshift/api/project/v1/generated.proto", fileDescriptor_fbf46eaac05029bf) +} + +var fileDescriptor_fbf46eaac05029bf = []byte{ + // 570 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x93, 0x3d, 0x8f, 0xd3, 0x30, + 0x18, 0xc7, 0x9b, 0xf6, 0x7a, 0x5c, 0x5d, 0xee, 0x84, 0xc2, 0x52, 0x75, 0x48, 0x4b, 0x90, 0x50, + 0x07, 0x70, 0x68, 0x79, 0x11, 0x73, 0x40, 0x08, 0x24, 0x5e, 0x0e, 0xb3, 0x55, 0x0c, 0xb8, 0xa9, + 0x9b, 0x9a, 0x5e, 0x62, 0x13, 0xbb, 0x95, 0x8e, 0x89, 0x8f, 0xc0, 0xce, 0xe7, 0x60, 0x65, 0xee, + 0x78, 0xe3, 0x4d, 0xd5, 0x35, 0x7c, 0x8b, 0x9b, 0x90, 0x1d, 0x37, 0x09, 0x5c, 0x91, 0xee, 0x16, + 0xb6, 0xfa, 0xc9, 0xff, 0xf7, 0xb3, 0xfd, 0x3c, 0x2e, 0x78, 0x10, 0x52, 0x39, 0x9d, 0x8f, 0x60, + 0xc0, 0x22, 0x8f, 0x71, 0x12, 0x8b, 0x29, 0x9d, 0x48, 0x0f, 0x73, 0xea, 0xf1, 0x84, 0x7d, 0x22, + 0x81, 0xf4, 0x16, 0x7d, 0x2f, 0x24, 0x31, 0x49, 0xb0, 0x24, 0x63, 0xc8, 0x13, 0x26, 0x99, 0x7d, + 0xbb, 0x80, 0x60, 0x0e, 0x41, 0xcc, 0x29, 0x34, 0x10, 0x5c, 0xf4, 0xdb, 0xf7, 0x4a, 0xe6, 0x90, + 0x85, 0xcc, 0xd3, 0xec, 0x68, 0x3e, 0xd1, 0x2b, 0xbd, 0xd0, 0xbf, 0x32, 0x67, 0xdb, 0x9d, 0x3d, + 0x11, 0x90, 0x32, 0xbd, 0x75, 0xc0, 0x12, 0xb2, 0x65, 0xdf, 0xf6, 0xc3, 0x22, 0x13, 0xe1, 0x60, + 0x4a, 0x63, 0x92, 0x1c, 0x7b, 0x7c, 0x16, 0xaa, 0x82, 0xf0, 0x22, 0x22, 0xf1, 0x36, 0xea, 0xf1, + 0xbf, 0xa8, 0x64, 0x1e, 0x4b, 0x1a, 0x11, 0x4f, 0x04, 0x53, 0x12, 0xe1, 0xbf, 0x39, 0xf7, 0x7b, + 0x15, 0x5c, 0x3b, 0xcc, 0xee, 0x63, 0x7f, 0x04, 0x7b, 0x4a, 0x3f, 0xc6, 0x12, 0xb7, 0xac, 0xae, + 0xd5, 0x6b, 0x0e, 0xee, 0xc3, 0x4c, 0x0b, 0xcb, 0x5a, 0xc8, 0x67, 0xa1, 0x2a, 0x08, 0xa8, 0xd2, + 0x70, 0xd1, 0x87, 0x6f, 0x47, 0x8a, 0x7f, 0x4d, 0x24, 0xf6, 0xed, 0xe5, 0xaa, 0x53, 0x49, 0x57, + 0x1d, 0x50, 0xd4, 0x50, 0x6e, 0xb5, 0x11, 0xd8, 0x11, 0x9c, 0x04, 0xad, 0xaa, 0xb1, 0x5f, 0xa2, + 0xc5, 0xd0, 0x9c, 0xee, 0x3d, 0x27, 0x81, 0x7f, 0xdd, 0xd8, 0x77, 0xd4, 0x0a, 0x69, 0x97, 0x3d, + 0x04, 0xbb, 0x42, 0x62, 0x39, 0x17, 0xad, 0x9a, 0xb6, 0x0e, 0xae, 0x64, 0xd5, 0xa4, 0x7f, 0x60, + 0xbc, 0xbb, 0xd9, 0x1a, 0x19, 0xa3, 0xfb, 0xd3, 0x02, 0x4d, 0x93, 0x7c, 0x45, 0x85, 0xb4, 0x3f, + 0x5c, 0xe8, 0x10, 0xbc, 0x5c, 0x87, 0x14, 0xad, 0xfb, 0x73, 0xc3, 0xec, 0xb4, 0xb7, 0xa9, 0x94, + 0xba, 0xf3, 0x0e, 0xd4, 0xa9, 0x24, 0x91, 0x68, 0x55, 0xbb, 0xb5, 0x5e, 0x73, 0x70, 0xf7, 0x2a, + 0x17, 0xf1, 0xf7, 0x8d, 0xb8, 0xfe, 0x52, 0x29, 0x50, 0x66, 0x72, 0xcf, 0x2c, 0x70, 0x60, 0x12, + 0x88, 0x7c, 0x9e, 0x13, 0xf1, 0x3f, 0xa6, 0xfc, 0x08, 0x34, 0xc7, 0x54, 0xf0, 0x23, 0x7c, 0xfc, + 0x06, 0x47, 0x44, 0x0f, 0xbb, 0xe1, 0xdf, 0x34, 0x48, 0xf3, 0x59, 0xf1, 0x09, 0x95, 0x73, 0x1a, + 0x23, 0x22, 0x48, 0x28, 0x97, 0x94, 0xc5, 0x7a, 0x9a, 0x65, 0xac, 0xf8, 0x84, 0xca, 0x39, 0x17, + 0xe7, 0x23, 0x52, 0x8f, 0xc2, 0x46, 0x00, 0x4c, 0x68, 0x8c, 0x8f, 0xe8, 0x17, 0x92, 0x88, 0x96, + 0xd5, 0xad, 0xf5, 0x1a, 0xfe, 0x40, 0x1d, 0xf5, 0x79, 0x5e, 0x3d, 0x5f, 0x75, 0xba, 0x17, 0xff, + 0x88, 0x30, 0x0f, 0xe8, 0xa3, 0x95, 0x2c, 0xee, 0x0f, 0x0b, 0xec, 0xff, 0xf1, 0x60, 0xec, 0x17, + 0xa0, 0xce, 0xa7, 0x58, 0x10, 0xdd, 0xc1, 0x86, 0x3f, 0xd8, 0x34, 0xff, 0x50, 0x15, 0xcf, 0x57, + 0x9d, 0x5b, 0x5b, 0xfc, 0x4a, 0x2b, 0x38, 0x0e, 0x88, 0x0e, 0xa1, 0x4c, 0x60, 0x0f, 0x01, 0x08, + 0x58, 0x3c, 0xa6, 0xea, 0x2e, 0x9b, 0xc9, 0xdf, 0x29, 0x0d, 0x04, 0x2a, 0x1c, 0x96, 0xf1, 0xa7, + 0x9b, 0x78, 0x31, 0x86, 0xbc, 0x24, 0x50, 0xc9, 0xe6, 0xf7, 0x96, 0x6b, 0xa7, 0x72, 0xb2, 0x76, + 0x2a, 0xa7, 0x6b, 0xa7, 0xf2, 0x35, 0x75, 0xac, 0x65, 0xea, 0x58, 0x27, 0xa9, 0x63, 0x9d, 0xa6, + 0x8e, 0x75, 0x96, 0x3a, 0xd6, 0xb7, 0x5f, 0x4e, 0x65, 0x58, 0x5d, 0xf4, 0x7f, 0x07, 0x00, 0x00, + 0xff, 0xff, 0x0a, 0xd0, 0xf2, 0xe0, 0x22, 0x05, 0x00, 0x00, +} + +func (m *Project) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Project) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Project) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ProjectList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProjectList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProjectList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ProjectRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProjectRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProjectRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x1a + i -= len(m.DisplayName) + copy(dAtA[i:], m.DisplayName) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.DisplayName))) + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ProjectSpec) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProjectSpec) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProjectSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Finalizers) > 0 { + for iNdEx := len(m.Finalizers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Finalizers[iNdEx]) + copy(dAtA[i:], m.Finalizers[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Finalizers[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ProjectStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProjectStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProjectStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Conditions) > 0 { + for iNdEx := len(m.Conditions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Conditions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + i -= len(m.Phase) + copy(dAtA[i:], m.Phase) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Phase))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { + offset -= sovGenerated(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Project) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Spec.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *ProjectList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *ProjectRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.DisplayName) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Description) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *ProjectSpec) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Finalizers) > 0 { + for _, s := range m.Finalizers { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *ProjectStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Phase) + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Conditions) > 0 { + for _, e := range m.Conditions { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func sovGenerated(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenerated(x uint64) (n int) { + return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Project) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Project{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "ProjectSpec", "ProjectSpec", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(this.Status.String(), "ProjectStatus", "ProjectStatus", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *ProjectList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]Project{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "Project", "Project", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&ProjectList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *ProjectRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ProjectRequest{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `DisplayName:` + fmt.Sprintf("%v", this.DisplayName) + `,`, + `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `}`, + }, "") + return s +} +func (this *ProjectSpec) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ProjectSpec{`, + `Finalizers:` + fmt.Sprintf("%v", this.Finalizers) + `,`, + `}`, + }, "") + return s +} +func (this *ProjectStatus) String() string { + if this == nil { + return "nil" + } + repeatedStringForConditions := "[]NamespaceCondition{" + for _, f := range this.Conditions { + repeatedStringForConditions += fmt.Sprintf("%v", f) + "," + } + repeatedStringForConditions += "}" + s := strings.Join([]string{`&ProjectStatus{`, + `Phase:` + fmt.Sprintf("%v", this.Phase) + `,`, + `Conditions:` + repeatedStringForConditions + `,`, + `}`, + }, "") + return s +} +func valueToStringGenerated(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Project) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Project: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Project: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProjectList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProjectList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProjectList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, Project{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProjectRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProjectRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProjectRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisplayName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DisplayName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProjectSpec) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProjectSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProjectSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Finalizers", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Finalizers = append(m.Finalizers, k8s_io_api_core_v1.FinalizerName(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProjectStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProjectStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProjectStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Phase", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Phase = k8s_io_api_core_v1.NamespacePhase(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Conditions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Conditions = append(m.Conditions, v11.NamespaceCondition{}) + if err := m.Conditions[len(m.Conditions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenerated(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenerated + } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthGenerated + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipGenerated(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthGenerated + } + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") +) diff --git a/vendor/github.com/openshift/api/project/v1/generated.proto b/vendor/github.com/openshift/api/project/v1/generated.proto new file mode 100644 index 000000000..dec0f1e4b --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/generated.proto @@ -0,0 +1,77 @@ + +// This file was autogenerated by go-to-protobuf. Do not edit it manually! + +syntax = 'proto2'; + +package github.com.openshift.api.project.v1; + +import "k8s.io/api/core/v1/generated.proto"; +import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; +import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; + +// Package-wide variables from generator "generated". +option go_package = "v1"; + +// Projects are the unit of isolation and collaboration in OpenShift. A project has one or more members, +// a quota on the resources that the project may consume, and the security controls on the resources in +// the project. Within a project, members may have different roles - project administrators can set +// membership, editors can create and manage the resources, and viewers can see but not access running +// containers. In a normal cluster project administrators are not able to alter their quotas - that is +// restricted to cluster administrators. +// +// Listing or watching projects will return only projects the user has the reader role on. +// +// An OpenShift project is an alternative representation of a Kubernetes namespace. Projects are exposed +// as editable to end users while namespaces are not. Direct creation of a project is typically restricted +// to administrators, while end users should use the requestproject resource. +message Project { + // Standard object's metadata. + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + // Spec defines the behavior of the Namespace. + optional ProjectSpec spec = 2; + + // Status describes the current status of a Namespace + optional ProjectStatus status = 3; +} + +// ProjectList is a list of Project objects. +message ProjectList { + // Standard object's metadata. + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + + // Items is the list of projects + repeated Project items = 2; +} + +// ProjecRequest is the set of options necessary to fully qualify a project request +message ProjectRequest { + // Standard object's metadata. + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + // DisplayName is the display name to apply to a project + optional string displayName = 2; + + // Description is the description to apply to a project + optional string description = 3; +} + +// ProjectSpec describes the attributes on a Project +message ProjectSpec { + // Finalizers is an opaque list of values that must be empty to permanently remove object from storage + repeated string finalizers = 1; +} + +// ProjectStatus is information about the current status of a Project +message ProjectStatus { + // Phase is the current lifecycle phase of the project + // +optional + optional string phase = 1; + + // Represents the latest available observations of the project current state. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + repeated k8s.io.api.core.v1.NamespaceCondition conditions = 2; +} + diff --git a/vendor/github.com/openshift/api/project/v1/legacy.go b/vendor/github.com/openshift/api/project/v1/legacy.go new file mode 100644 index 000000000..186f905f3 --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/legacy.go @@ -0,0 +1,23 @@ +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + legacyGroupVersion = schema.GroupVersion{Group: "", Version: "v1"} + legacySchemeBuilder = runtime.NewSchemeBuilder(addLegacyKnownTypes, corev1.AddToScheme) + DeprecatedInstallWithoutGroup = legacySchemeBuilder.AddToScheme +) + +func addLegacyKnownTypes(scheme *runtime.Scheme) error { + types := []runtime.Object{ + &Project{}, + &ProjectList{}, + &ProjectRequest{}, + } + scheme.AddKnownTypes(legacyGroupVersion, types...) + return nil +} diff --git a/vendor/github.com/openshift/api/project/v1/register.go b/vendor/github.com/openshift/api/project/v1/register.go new file mode 100644 index 000000000..e471716ce --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/register.go @@ -0,0 +1,40 @@ +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + GroupName = "project.openshift.io" + GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, corev1.AddToScheme) + // Install is a function which adds this version to a scheme + Install = schemeBuilder.AddToScheme + + // SchemeGroupVersion generated code relies on this name + // Deprecated + SchemeGroupVersion = GroupVersion + // AddToScheme exists solely to keep the old generators creating valid code + // DEPRECATED + AddToScheme = schemeBuilder.AddToScheme +) + +// Resource generated code relies on this being here, but it logically belongs to the group +// DEPRECATED +func Resource(resource string) schema.GroupResource { + return schema.GroupResource{Group: GroupName, Resource: resource} +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, + &Project{}, + &ProjectList{}, + &ProjectRequest{}, + ) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/vendor/github.com/openshift/api/project/v1/types.go b/vendor/github.com/openshift/api/project/v1/types.go new file mode 100644 index 000000000..51829507a --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/types.go @@ -0,0 +1,93 @@ +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ProjectList is a list of Project objects. +type ProjectList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + // Items is the list of projects + Items []Project `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +const ( + // These are internal finalizer values to Origin + FinalizerOrigin corev1.FinalizerName = "openshift.io/origin" + // ProjectNodeSelector is an annotation that holds the node selector; + // the node selector annotation determines which nodes will have pods from this project scheduled to them + ProjectNodeSelector = "openshift.io/node-selector" + + // ProjectRequesterAnnotation is the username that requested a given project. Its not guaranteed to be present, + // but it is set by the default project template. + ProjectRequesterAnnotation = "openshift.io/requester" +) + +// ProjectSpec describes the attributes on a Project +type ProjectSpec struct { + // Finalizers is an opaque list of values that must be empty to permanently remove object from storage + Finalizers []corev1.FinalizerName `json:"finalizers,omitempty" protobuf:"bytes,1,rep,name=finalizers,casttype=k8s.io/api/core/v1.FinalizerName"` +} + +// ProjectStatus is information about the current status of a Project +type ProjectStatus struct { + // Phase is the current lifecycle phase of the project + // +optional + Phase corev1.NamespacePhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=k8s.io/api/core/v1.NamespacePhase"` + + // Represents the latest available observations of the project current state. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions []corev1.NamespaceCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,2,rep,name=conditions"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Projects are the unit of isolation and collaboration in OpenShift. A project has one or more members, +// a quota on the resources that the project may consume, and the security controls on the resources in +// the project. Within a project, members may have different roles - project administrators can set +// membership, editors can create and manage the resources, and viewers can see but not access running +// containers. In a normal cluster project administrators are not able to alter their quotas - that is +// restricted to cluster administrators. +// +// Listing or watching projects will return only projects the user has the reader role on. +// +// An OpenShift project is an alternative representation of a Kubernetes namespace. Projects are exposed +// as editable to end users while namespaces are not. Direct creation of a project is typically restricted +// to administrators, while end users should use the requestproject resource. +type Project struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Spec defines the behavior of the Namespace. + Spec ProjectSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + + // Status describes the current status of a Namespace + Status ProjectStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +genclient +// +genclient:nonNamespaced +// +genclient:skipVerbs=get,list,create,update,patch,delete,deleteCollection,watch +// +genclient:method=Create,verb=create,result=Project + +// ProjecRequest is the set of options necessary to fully qualify a project request +type ProjectRequest struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + // DisplayName is the display name to apply to a project + DisplayName string `json:"displayName,omitempty" protobuf:"bytes,2,opt,name=displayName"` + // Description is the description to apply to a project + Description string `json:"description,omitempty" protobuf:"bytes,3,opt,name=description"` +} diff --git a/vendor/github.com/openshift/api/project/v1/zz_generated.deepcopy.go b/vendor/github.com/openshift/api/project/v1/zz_generated.deepcopy.go new file mode 100644 index 000000000..763383030 --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/zz_generated.deepcopy.go @@ -0,0 +1,141 @@ +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Project) DeepCopyInto(out *Project) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Project. +func (in *Project) DeepCopy() *Project { + if in == nil { + return nil + } + out := new(Project) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Project) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectList) DeepCopyInto(out *ProjectList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Project, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectList. +func (in *ProjectList) DeepCopy() *ProjectList { + if in == nil { + return nil + } + out := new(ProjectList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProjectList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectRequest) DeepCopyInto(out *ProjectRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectRequest. +func (in *ProjectRequest) DeepCopy() *ProjectRequest { + if in == nil { + return nil + } + out := new(ProjectRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProjectRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) { + *out = *in + if in.Finalizers != nil { + in, out := &in.Finalizers, &out.Finalizers + *out = make([]corev1.FinalizerName, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectSpec. +func (in *ProjectSpec) DeepCopy() *ProjectSpec { + if in == nil { + return nil + } + out := new(ProjectSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectStatus) DeepCopyInto(out *ProjectStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]corev1.NamespaceCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectStatus. +func (in *ProjectStatus) DeepCopy() *ProjectStatus { + if in == nil { + return nil + } + out := new(ProjectStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/openshift/api/project/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/project/v1/zz_generated.swagger_doc_generated.go new file mode 100644 index 000000000..295892251 --- /dev/null +++ b/vendor/github.com/openshift/api/project/v1/zz_generated.swagger_doc_generated.go @@ -0,0 +1,65 @@ +package v1 + +// This file contains a collection of methods that can be used from go-restful to +// generate Swagger API documentation for its models. Please read this PR for more +// information on the implementation: https://github.com/emicklei/go-restful/pull/215 +// +// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if +// they are on one line! For multiple line or blocks that you want to ignore use ---. +// Any context after a --- is ignored. +// +// Those methods can be generated by using hack/update-swagger-docs.sh + +// AUTO-GENERATED FUNCTIONS START HERE +var map_Project = map[string]string{ + "": "Projects are the unit of isolation and collaboration in OpenShift. A project has one or more members, a quota on the resources that the project may consume, and the security controls on the resources in the project. Within a project, members may have different roles - project administrators can set membership, editors can create and manage the resources, and viewers can see but not access running containers. In a normal cluster project administrators are not able to alter their quotas - that is restricted to cluster administrators.\n\nListing or watching projects will return only projects the user has the reader role on.\n\nAn OpenShift project is an alternative representation of a Kubernetes namespace. Projects are exposed as editable to end users while namespaces are not. Direct creation of a project is typically restricted to administrators, while end users should use the requestproject resource.", + "metadata": "Standard object's metadata.", + "spec": "Spec defines the behavior of the Namespace.", + "status": "Status describes the current status of a Namespace", +} + +func (Project) SwaggerDoc() map[string]string { + return map_Project +} + +var map_ProjectList = map[string]string{ + "": "ProjectList is a list of Project objects.", + "metadata": "Standard object's metadata.", + "items": "Items is the list of projects", +} + +func (ProjectList) SwaggerDoc() map[string]string { + return map_ProjectList +} + +var map_ProjectRequest = map[string]string{ + "": "ProjecRequest is the set of options necessary to fully qualify a project request", + "metadata": "Standard object's metadata.", + "displayName": "DisplayName is the display name to apply to a project", + "description": "Description is the description to apply to a project", +} + +func (ProjectRequest) SwaggerDoc() map[string]string { + return map_ProjectRequest +} + +var map_ProjectSpec = map[string]string{ + "": "ProjectSpec describes the attributes on a Project", + "finalizers": "Finalizers is an opaque list of values that must be empty to permanently remove object from storage", +} + +func (ProjectSpec) SwaggerDoc() map[string]string { + return map_ProjectSpec +} + +var map_ProjectStatus = map[string]string{ + "": "ProjectStatus is information about the current status of a Project", + "phase": "Phase is the current lifecycle phase of the project", + "conditions": "Represents the latest available observations of the project current state.", +} + +func (ProjectStatus) SwaggerDoc() map[string]string { + return map_ProjectStatus +} + +// AUTO-GENERATED FUNCTIONS END HERE diff --git a/vendor/modules.txt b/vendor/modules.txt index 265de69b8..6cd223188 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -121,6 +121,7 @@ github.com/onsi/gomega/types github.com/openshift/api/config/v1 github.com/openshift/api/console/v1 github.com/openshift/api/oauth/v1 +github.com/openshift/api/project/v1 github.com/openshift/api/route/v1 github.com/openshift/api/user/v1 # github.com/operator-framework/api v0.10.0 => github.com/operator-framework/api v0.8.0