From 76bd462cf8c8c07efc89eb0c941836d0c6a62aa7 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Mon, 5 Oct 2020 17:28:19 -0700 Subject: [PATCH 01/28] Implement very rough skeleton of the start of a supervisor server - This is just stab at a starting place because it felt easier to put something down on paper than to keep staring at a blank page --- Dockerfile | 2 + cmd/pinniped-supervisor/main.go | 186 ++++++++++++++++++ deploy-supervisor/README.md | 41 ++++ deploy-supervisor/deployment.yaml | 146 ++++++++++++++ deploy-supervisor/rbac.yaml | 34 ++++ deploy-supervisor/values.yaml | 22 +++ hack/prepare-for-integration-tests.sh | 20 +- .../dynamic_config_watcher.go | 52 +++++ 8 files changed, 500 insertions(+), 3 deletions(-) create mode 100644 cmd/pinniped-supervisor/main.go create mode 100644 deploy-supervisor/README.md create mode 100644 deploy-supervisor/deployment.yaml create mode 100644 deploy-supervisor/rbac.yaml create mode 100644 deploy-supervisor/values.yaml create mode 100644 internal/controller/supervisorconfig/dynamic_config_watcher.go diff --git a/Dockerfile b/Dockerfile index d95684d3b..b5dc23a1f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ COPY hack ./hack # Build the executable binary (CGO_ENABLED=0 means static linking) RUN mkdir out \ && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(hack/get-ldflags.sh)" -o out ./cmd/pinniped-server/... \ + && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(hack/get-ldflags.sh)" -o out ./cmd/pinniped-supervisor/... \ && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o out ./cmd/local-user-authenticator/... # Use a runtime image based on Debian slim @@ -28,6 +29,7 @@ FROM debian:10.5-slim # Copy the binaries from the build-env stage COPY --from=build-env /work/out/pinniped-server /usr/local/bin/pinniped-server +COPY --from=build-env /work/out/pinniped-supervisor /usr/local/bin/pinniped-supervisor COPY --from=build-env /work/out/local-user-authenticator /usr/local/bin/local-user-authenticator # Document the port diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go new file mode 100644 index 000000000..c15ef7a14 --- /dev/null +++ b/cmd/pinniped-supervisor/main.go @@ -0,0 +1,186 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "os/signal" + "time" + + kubeinformers "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/pkg/version" + "k8s.io/client-go/rest" + restclient "k8s.io/client-go/rest" + "k8s.io/component-base/logs" + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" + + "go.pinniped.dev/internal/controller/supervisorconfig" + "go.pinniped.dev/internal/controllerlib" + "go.pinniped.dev/internal/downward" +) + +const ( + singletonWorker = 1 + defaultResyncInterval = 3 * time.Minute +) + +type helloWorld struct{} + +func (w *helloWorld) start(ctx context.Context, l net.Listener) error { + server := http.Server{ + Handler: w, + } + + errCh := make(chan error) + go func() { + errCh <- server.Serve(l) + }() + + go func() { + select { + case err := <-errCh: + klog.InfoS("server exited", "err", err) + case <-ctx.Done(): + klog.InfoS("server context cancelled", "err", ctx.Err()) + if err := server.Shutdown(context.Background()); err != nil { + klog.InfoS("server shutdown failed", "err", err) + } + } + }() + + return nil +} + +func (w *helloWorld) ServeHTTP(rsp http.ResponseWriter, req *http.Request) { + // TODO this is just a placeholder to allow manually testing that this is reachable; we don't want a hello world endpoint + defer req.Body.Close() + _, _ = fmt.Fprintf(rsp, "Hello, world!") +} + +func waitForSignal() os.Signal { + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, os.Interrupt) + return <-signalCh +} + +func startControllers( + ctx context.Context, + kubeClient kubernetes.Interface, + kubeInformers kubeinformers.SharedInformerFactory, + serverInstallationNamespace string, + staticConfig StaticConfig, +) { + // Create controller manager. + controllerManager := controllerlib. + NewManager(). + WithController( + supervisorconfig.NewDynamicConfigWatcherController( + serverInstallationNamespace, + staticConfig.NamesConfig.DynamicConfigMap, + kubeClient, + kubeInformers.Core().V1().ConfigMaps(), + controllerlib.WithInformer, + ), + singletonWorker, + ) + + kubeInformers.Start(ctx.Done()) + + go controllerManager.Start(ctx) +} + +func newK8sClient() (kubernetes.Interface, error) { + kubeConfig, err := restclient.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("could not load in-cluster configuration: %w", err) + } + + // Connect to the core Kubernetes API. + kubeClient, err := kubernetes.NewForConfig(kubeConfig) + if err != nil { + return nil, fmt.Errorf("could not load in-cluster configuration: %w", err) + } + + return kubeClient, nil +} + +func run(serverInstallationNamespace string, staticConfig StaticConfig) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + kubeClient, err := newK8sClient() + if err != nil { + return fmt.Errorf("cannot create k8s client: %w", err) + } + + kubeInformers := kubeinformers.NewSharedInformerFactoryWithOptions( + kubeClient, + defaultResyncInterval, + kubeinformers.WithNamespace(serverInstallationNamespace), + ) + + startControllers(ctx, kubeClient, kubeInformers, serverInstallationNamespace, staticConfig) + + //nolint: gosec // Intentionally binding to all network interfaces. + l, err := net.Listen("tcp", ":80") + if err != nil { + return fmt.Errorf("cannot create listener: %w", err) + } + defer l.Close() + + helloHandler := &helloWorld{} + err = helloHandler.start(ctx, l) + if err != nil { + return fmt.Errorf("cannot start webhook: %w", err) + } + klog.InfoS("supervisor is ready", "address", l.Addr().String()) + + gotSignal := waitForSignal() + klog.InfoS("supervisor exiting", "signal", gotSignal) + + return nil +} + +type StaticConfig struct { + NamesConfig NamesConfigSpec `json:"names"` +} + +type NamesConfigSpec struct { + DynamicConfigMap string `json:"dynamicConfigMap"` +} + +func main() { + logs.InitLogs() + defer logs.FlushLogs() + + klog.Infof("Running %s at %#v", rest.DefaultKubernetesUserAgent(), version.Get()) + klog.Infof("Command-line arguments were: %s %s %s", os.Args[0], os.Args[1], os.Args[2]) + + // Discover in which namespace we are installed. + podInfo, err := downward.Load(os.Args[1]) + if err != nil { + klog.Fatal(fmt.Errorf("could not read pod metadata: %w", err)) + } + + // Read static config. + data, err := ioutil.ReadFile(os.Args[2]) + if err != nil { + klog.Fatal(fmt.Errorf("read file: %w", err)) + } + var staticConfig StaticConfig + if err := yaml.Unmarshal(data, &staticConfig); err != nil { + klog.Fatal(fmt.Errorf("decode yaml: %w", err)) + } + + if err := run(podInfo.Namespace, staticConfig); err != nil { + klog.Fatal(err) + } +} diff --git a/deploy-supervisor/README.md b/deploy-supervisor/README.md new file mode 100644 index 000000000..0a1c31061 --- /dev/null +++ b/deploy-supervisor/README.md @@ -0,0 +1,41 @@ +# Deploying the Pinniped Supervisor + +## What is the Pinniped Supervisor? + +The Pinniped Supervisor app is a component of the Pinniped OIDC and Cluster Federation solutions. +It can be deployed when those features are needed. + +## Installing the Latest Version with Default Options + +```bash +kubectl apply -f https://github.com/vmware-tanzu/pinniped/releases/latest/download/install-supervisor.yaml +``` + +## Installing an Older Version with Default Options + +Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) version number +and use it to replace the version number in the URL below. + +```bash +# Replace v0.3.0 with your preferred version in the URL below +kubectl apply -f https://github.com/vmware-tanzu/pinniped/releases/download/v0.3.0/install-supervisor.yaml +``` + +## Installing with Custom Options + +Creating your own deployment YAML file requires `ytt` from [Carvel](https://carvel.dev/) to template the YAML files +in the [deploy-supervisor](../deploy-supervisor) directory. +Either [install `ytt`](https://get-ytt.io/) or use the [container image from Dockerhub](https://hub.docker.com/r/k14s/image/tags). + +1. `git clone` this repo and `git checkout` the release version tag of the release that you would like to deploy. +1. The configuration options are in [deploy-supervisor/values.yml](values.yaml). + Fill in the values in that file, or override those values using additional `ytt` command-line options in + the command below. Use the release version tag as the `image_tag` value. +2. In a terminal, cd to this `deploy-supervisor` directory +3. To generate the final YAML files, run `ytt --file .` +4. Deploy the generated YAML using your preferred deployment tool, such as `kubectl` or [`kapp`](https://get-kapp.io/). + For example: `ytt --file . | kapp deploy --yes --app pinniped-supervisor --diff-changes --file -` + +## Configuring After Installing + +TODO: Provide some instructions here. diff --git a/deploy-supervisor/deployment.yaml b/deploy-supervisor/deployment.yaml new file mode 100644 index 000000000..cd4079c8a --- /dev/null +++ b/deploy-supervisor/deployment.yaml @@ -0,0 +1,146 @@ +#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@ load("@ytt:data", "data") + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: #@ data.values.namespace + labels: + name: #@ data.values.namespace +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: #@ data.values.app_name + namespace: #@ data.values.namespace +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: #@ data.values.app_name + "-static-config" + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +data: + #@yaml/text-templated-strings + pinniped.yaml: | + names: + dynamicConfigMap: (@= data.values.app_name + "-dynamic-config" @) +--- +#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "": +apiVersion: v1 +kind: Secret +metadata: + name: image-pull-secret + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: #@ data.values.image_pull_dockerconfigjson +#@ end +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: #@ data.values.app_name + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +spec: + replicas: #@ data.values.replicas + selector: + matchLabels: + app: #@ data.values.app_name + template: + metadata: + labels: + app: #@ data.values.app_name + spec: + serviceAccountName: #@ data.values.app_name + #@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "": + imagePullSecrets: + - name: image-pull-secret + #@ end + containers: + - name: pinniped-supervisor + #@ if data.values.image_digest: + image: #@ data.values.image_repo + "@" + data.values.image_digest + #@ else: + image: #@ data.values.image_repo + ":" + data.values.image_tag + #@ end + imagePullPolicy: IfNotPresent + command: #! override the default entrypoint + - /usr/local/bin/pinniped-supervisor + args: + - /etc/podinfo #! TODO proper flag parsing instead of positional + - /etc/config/pinniped.yaml #! TODO proper flag parsing instead of positional + resources: + requests: + memory: "128Mi" + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: podinfo + mountPath: /etc/podinfo +#! livenessProbe: +#! httpGet: +#! path: /healthz +#! port: 443 +#! scheme: HTTPS +#! initialDelaySeconds: 2 +#! timeoutSeconds: 15 +#! periodSeconds: 10 +#! failureThreshold: 5 +#! readinessProbe: +#! httpGet: +#! path: /healthz +#! port: 443 +#! scheme: HTTPS +#! initialDelaySeconds: 2 +#! timeoutSeconds: 3 +#! periodSeconds: 10 +#! failureThreshold: 3 + volumes: + - name: config-volume + configMap: + name: #@ data.values.app_name + "-static-config" + - name: podinfo + downwardAPI: + items: + - path: "labels" + fieldRef: + fieldPath: metadata.labels + - path: "namespace" + fieldRef: + fieldPath: metadata.namespace + #! This will help make sure our multiple pods run on different nodes, making + #! our deployment "more" "HA". + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 50 + podAffinityTerm: + labelSelector: + matchLabels: + app: #@ data.values.app_name + topologyKey: kubernetes.io/hostname +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ data.values.app_name + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +spec: + type: ClusterIP + selector: + app: #@ data.values.app_name + ports: + - protocol: TCP + port: 80 + targetPort: 80 diff --git a/deploy-supervisor/rbac.yaml b/deploy-supervisor/rbac.yaml new file mode 100644 index 000000000..ecba850b2 --- /dev/null +++ b/deploy-supervisor/rbac.yaml @@ -0,0 +1,34 @@ +#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@ load("@ytt:data", "data") + +#! Give permission to various objects within the app's own namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: #@ data.values.app_name + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +rules: + - apiGroups: [""] + resources: [configmaps] + verbs: [get, list, watch] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ data.values.app_name + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +subjects: + - kind: ServiceAccount + name: #@ data.values.app_name + namespace: #@ data.values.namespace +roleRef: + kind: Role + name: #@ data.values.app_name + apiGroup: rbac.authorization.k8s.io diff --git a/deploy-supervisor/values.yaml b/deploy-supervisor/values.yaml new file mode 100644 index 000000000..6df6efe99 --- /dev/null +++ b/deploy-supervisor/values.yaml @@ -0,0 +1,22 @@ +#! Copyright 2020 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@data/values +--- + +app_name: pinniped-supervisor +namespace: pinniped-supervisor + +#! Specify how many replicas of the Pinniped server to run. +replicas: 2 + +#! Specify either an image_digest or an image_tag. If both are given, only image_digest will be used. +image_repo: docker.io/getpinniped/pinniped-server +image_digest: #! e.g. sha256:f3c4fdfd3ef865d4b97a1fd295d94acc3f0c654c46b6f27ffad5cf80216903c8 +image_tag: latest + +#! Specifies a secret to be used when pulling the above container image. +#! Can be used when the above image_repo is a private registry. +#! Typically the value would be the output of: kubectl create secret docker-registry x --docker-server=https://example.io --docker-username="USERNAME" --docker-password="PASSWORD" --dry-run=client -o json | jq -r '.data[".dockerconfigjson"]' +#! Optional. +image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}} diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index 8340f2e48..05f942881 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -174,15 +174,29 @@ kubectl create secret generic "$test_username" \ --output yaml | kubectl apply -f - +# +# Deploy the Pinniped Supervisor +# +pushd deploy-supervisor >/dev/null + +log_note "Deploying the Pinniped Supervisor app to the cluster..." +ytt --file . \ + --data-value "image_repo=$registry_repo" \ + --data-value "image_tag=$tag" >"$manifest" + +kapp deploy --yes --app "pinniped-supervisor" --diff-changes --file "$manifest" + +popd >/dev/null + +# +# Deploy Pinniped +# app_name="pinniped" namespace="integration" webhook_url="https://local-user-authenticator.local-user-authenticator.svc/authenticate" webhook_ca_bundle="$(kubectl get secret local-user-authenticator-tls-serving-certificate --namespace local-user-authenticator -o 'jsonpath={.data.caCertificate}')" discovery_url="$(TERM=dumb kubectl cluster-info | awk '/Kubernetes master/ {print $NF}')" -# -# Deploy Pinniped -# pushd deploy >/dev/null log_note "Deploying the Pinniped app to the cluster..." diff --git a/internal/controller/supervisorconfig/dynamic_config_watcher.go b/internal/controller/supervisorconfig/dynamic_config_watcher.go new file mode 100644 index 000000000..10319a177 --- /dev/null +++ b/internal/controller/supervisorconfig/dynamic_config_watcher.go @@ -0,0 +1,52 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package supervisorconfig + +import ( + corev1informers "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + + pinnipedcontroller "go.pinniped.dev/internal/controller" + "go.pinniped.dev/internal/controllerlib" +) + +type dynamicConfigWatcherController struct { + k8sClient kubernetes.Interface + configMapInformer corev1informers.ConfigMapInformer +} + +func NewDynamicConfigWatcherController( + serverInstallationNamespace string, + configMapName string, + k8sClient kubernetes.Interface, + configMapInformer corev1informers.ConfigMapInformer, + withInformer pinnipedcontroller.WithInformerOptionFunc, +) controllerlib.Controller { + return controllerlib.New( + controllerlib.Config{ + Name: "DynamicConfigWatcherController", + Syncer: &dynamicConfigWatcherController{ + k8sClient: k8sClient, + configMapInformer: configMapInformer, + }, + }, + withInformer( + configMapInformer, + pinnipedcontroller.NameAndNamespaceExactMatchFilterFactory(configMapName, serverInstallationNamespace), + controllerlib.InformerOption{}, + ), + ) +} + +// Sync implements controllerlib.Syncer. +func (c *dynamicConfigWatcherController) Sync(ctx controllerlib.Context) error { + // TODO Watch the configmap to find the issuer name, ingress url, etc. + // TODO Update some kind of in-memory representation of the configuration so the discovery endpoint can use it. + // TODO The discovery endpoint would return an error until all missing configuration options are filled in. + + klog.InfoS("DynamicConfigWatcherController sync finished") + + return nil +} From fd6a7f589290dbd77ef2651aca7fa028a2ac0461 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Tue, 6 Oct 2020 10:11:57 -0400 Subject: [PATCH 02/28] supervisor-oidc: hoist OIDC discovery handler for testing Signed-off-by: Andrew Keesler --- cmd/pinniped-supervisor/main.go | 30 +++---- .../dynamic_config_watcher.go | 72 +++++++++++++-- internal/oidc/discovery/discovery.go | 66 ++++++++++++++ internal/oidc/discovery/discovery_test.go | 90 +++++++++++++++++++ .../oidc/issuerprovider/issuerprovider.go | 32 +++++++ internal/oidc/oidc.go | 9 ++ 6 files changed, 275 insertions(+), 24 deletions(-) create mode 100644 internal/oidc/discovery/discovery.go create mode 100644 internal/oidc/discovery/discovery_test.go create mode 100644 internal/oidc/issuerprovider/issuerprovider.go create mode 100644 internal/oidc/oidc.go diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index c15ef7a14..d6fa97acd 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -25,6 +25,9 @@ import ( "go.pinniped.dev/internal/controller/supervisorconfig" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/downward" + "go.pinniped.dev/internal/oidc" + "go.pinniped.dev/internal/oidc/discovery" + "go.pinniped.dev/internal/oidc/issuerprovider" ) const ( @@ -32,11 +35,11 @@ const ( defaultResyncInterval = 3 * time.Minute ) -type helloWorld struct{} - -func (w *helloWorld) start(ctx context.Context, l net.Listener) error { +func start(ctx context.Context, l net.Listener, discoveryHandler http.Handler) { + mux := http.NewServeMux() + mux.Handle(oidc.WellKnownURLPath, discoveryHandler) server := http.Server{ - Handler: w, + Handler: mux, } errCh := make(chan error) @@ -55,14 +58,6 @@ func (w *helloWorld) start(ctx context.Context, l net.Listener) error { } } }() - - return nil -} - -func (w *helloWorld) ServeHTTP(rsp http.ResponseWriter, req *http.Request) { - // TODO this is just a placeholder to allow manually testing that this is reachable; we don't want a hello world endpoint - defer req.Body.Close() - _, _ = fmt.Fprintf(rsp, "Hello, world!") } func waitForSignal() os.Signal { @@ -73,6 +68,7 @@ func waitForSignal() os.Signal { func startControllers( ctx context.Context, + issuerProvider *issuerprovider.Provider, kubeClient kubernetes.Interface, kubeInformers kubeinformers.SharedInformerFactory, serverInstallationNamespace string, @@ -85,6 +81,7 @@ func startControllers( supervisorconfig.NewDynamicConfigWatcherController( serverInstallationNamespace, staticConfig.NamesConfig.DynamicConfigMap, + issuerProvider, kubeClient, kubeInformers.Core().V1().ConfigMaps(), controllerlib.WithInformer, @@ -127,7 +124,8 @@ func run(serverInstallationNamespace string, staticConfig StaticConfig) error { kubeinformers.WithNamespace(serverInstallationNamespace), ) - startControllers(ctx, kubeClient, kubeInformers, serverInstallationNamespace, staticConfig) + issuerProvider := issuerprovider.New() + startControllers(ctx, issuerProvider, kubeClient, kubeInformers, serverInstallationNamespace, staticConfig) //nolint: gosec // Intentionally binding to all network interfaces. l, err := net.Listen("tcp", ":80") @@ -136,11 +134,7 @@ func run(serverInstallationNamespace string, staticConfig StaticConfig) error { } defer l.Close() - helloHandler := &helloWorld{} - err = helloHandler.start(ctx, l) - if err != nil { - return fmt.Errorf("cannot start webhook: %w", err) - } + start(ctx, l, discovery.New(issuerProvider)) klog.InfoS("supervisor is ready", "address", l.Addr().String()) gotSignal := waitForSignal() diff --git a/internal/controller/supervisorconfig/dynamic_config_watcher.go b/internal/controller/supervisorconfig/dynamic_config_watcher.go index 10319a177..d1aa8e710 100644 --- a/internal/controller/supervisorconfig/dynamic_config_watcher.go +++ b/internal/controller/supervisorconfig/dynamic_config_watcher.go @@ -4,6 +4,9 @@ package supervisorconfig import ( + "fmt" + + k8serrors "k8s.io/apimachinery/pkg/api/errors" corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" @@ -12,14 +15,30 @@ import ( "go.pinniped.dev/internal/controllerlib" ) +const ( + issuerConfigMapKey = "issuer" +) + +// IssuerSetter can be notified of a valid issuer with its SetIssuer function. If there is no +// longer any valid issuer, then nil can be passed to this interface. +// +// Implementations of this type should be thread-safe to support calls from multiple goroutines. +type IssuerSetter interface { + SetIssuer(issuer *string) +} + type dynamicConfigWatcherController struct { - k8sClient kubernetes.Interface - configMapInformer corev1informers.ConfigMapInformer + configMapName string + configMapNamespace string + issuerSetter IssuerSetter + k8sClient kubernetes.Interface + configMapInformer corev1informers.ConfigMapInformer } func NewDynamicConfigWatcherController( serverInstallationNamespace string, configMapName string, + issuerObserver IssuerSetter, k8sClient kubernetes.Interface, configMapInformer corev1informers.ConfigMapInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, @@ -28,8 +47,11 @@ func NewDynamicConfigWatcherController( controllerlib.Config{ Name: "DynamicConfigWatcherController", Syncer: &dynamicConfigWatcherController{ - k8sClient: k8sClient, - configMapInformer: configMapInformer, + configMapNamespace: serverInstallationNamespace, + configMapName: configMapName, + issuerSetter: issuerObserver, + k8sClient: k8sClient, + configMapInformer: configMapInformer, }, }, withInformer( @@ -44,9 +66,47 @@ func NewDynamicConfigWatcherController( func (c *dynamicConfigWatcherController) Sync(ctx controllerlib.Context) error { // TODO Watch the configmap to find the issuer name, ingress url, etc. // TODO Update some kind of in-memory representation of the configuration so the discovery endpoint can use it. - // TODO The discovery endpoint would return an error until all missing configuration options are filled in. + // TODO The discovery endpoint would return an error until all missing configuration options are + // filled in. - klog.InfoS("DynamicConfigWatcherController sync finished") + configMap, err := c.configMapInformer. + Lister(). + ConfigMaps(c.configMapNamespace). + Get(c.configMapName) + notFound := k8serrors.IsNotFound(err) + if err != nil && !notFound { + return fmt.Errorf("failed to get %s/%s secret: %w", c.configMapNamespace, c.configMapName, err) + } + + if notFound { + klog.InfoS( + "dynamicConfigWatcherController Sync found no configmap", + "configmap", + klog.KRef(c.configMapNamespace, c.configMapName), + ) + c.issuerSetter.SetIssuer(nil) + return nil + } + + issuer, ok := configMap.Data[issuerConfigMapKey] + if !ok { + klog.InfoS( + "dynamicConfigWatcherController Sync found no issuer", + "configmap", + klog.KObj(configMap), + ) + c.issuerSetter.SetIssuer(nil) + return nil + } + + klog.InfoS( + "dynamicConfigWatcherController Sync issuer", + "configmap", + klog.KObj(configMap), + "issuer", + issuer, + ) + c.issuerSetter.SetIssuer(&issuer) return nil } diff --git a/internal/oidc/discovery/discovery.go b/internal/oidc/discovery/discovery.go new file mode 100644 index 000000000..a65f8c8db --- /dev/null +++ b/internal/oidc/discovery/discovery.go @@ -0,0 +1,66 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package discovery provides a handler for the OIDC discovery endpoint. +package discovery + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// Metadata holds all fields (that we care about) from the OpenID Provider Metadata section in the +// OpenID Connect Discovery specification: +// https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3. +type Metadata struct { + Issuer string `json:"issuer"` + + AuthorizationEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + JWKSURL string `json:"jwks_url"` + + ResponseTypesSupported []string `json:"response_types_supported"` + SubjectTypesSupported []string `json:"subject_types_supported"` + IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"` +} + +// IssuerGetter holds onto an issuer which can be retrieved via its GetIssuer function. If there is +// no valid issuer, then nil will be returned. +// +// Implementations of this type should be thread-safe to support calls from multiple goroutines. +type IssuerGetter interface { + GetIssuer() *string +} + +// New returns an http.Handler that will use information from the provided IssuerGetter to serve an +// OIDC discovery endpoint. +func New(ig IssuerGetter) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + if r.Method != http.MethodGet { + http.Error(w, `{"error": "Method not allowed (try GET)"}`, http.StatusMethodNotAllowed) + return + } + + issuer := ig.GetIssuer() + if issuer == nil { + http.Error(w, `{"error": "OIDC discovery not available (unknown issuer)"}`, http.StatusServiceUnavailable) + return + } + + oidcConfig := Metadata{ + Issuer: *issuer, + AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", *issuer), + TokenEndpoint: fmt.Sprintf("%s/oauth2/v0/token", *issuer), + JWKSURL: fmt.Sprintf("%s/oauth2/v0/keys", *issuer), + ResponseTypesSupported: []string{}, + SubjectTypesSupported: []string{}, + IDTokenSigningAlgValuesSupported: []string{}, + } + if err := json.NewEncoder(w).Encode(&oidcConfig); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) +} diff --git a/internal/oidc/discovery/discovery_test.go b/internal/oidc/discovery/discovery_test.go new file mode 100644 index 000000000..b89aab977 --- /dev/null +++ b/internal/oidc/discovery/discovery_test.go @@ -0,0 +1,90 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package discovery + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "go.pinniped.dev/internal/oidc/issuerprovider" +) + +func TestDiscovery(t *testing.T) { + tests := []struct { + name string + + issuer string + method string + + wantStatus int + wantContentType string + wantBody interface{} + }{ + { + name: "issuer returns nil issuer", + method: http.MethodGet, + wantStatus: http.StatusServiceUnavailable, + wantBody: map[string]string{ + "error": "OIDC discovery not available (unknown issuer)", + }, + }, + { + name: "issuer returns non-nil issuer", + issuer: "https://some-issuer.com", + method: http.MethodGet, + wantStatus: http.StatusOK, + wantContentType: "application/json", + wantBody: &Metadata{ + Issuer: "https://some-issuer.com", + AuthorizationEndpoint: "https://some-issuer.com/oauth2/v0/auth", + TokenEndpoint: "https://some-issuer.com/oauth2/v0/token", + JWKSURL: "https://some-issuer.com/oauth2/v0/keys", + ResponseTypesSupported: []string{}, + SubjectTypesSupported: []string{}, + IDTokenSigningAlgValuesSupported: []string{}, + }, + }, + { + name: "bad method", + issuer: "https://some-issuer.com", + method: http.MethodPost, + wantStatus: http.StatusMethodNotAllowed, + wantBody: map[string]string{ + "error": "Method not allowed (try GET)", + }, + }, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + p := issuerprovider.New() + if test.issuer != "" { + p.SetIssuer(&test.issuer) + } else { + p.SetIssuer(nil) + } + + handler := New(p) + req := httptest.NewRequest(test.method, "/this/path/shouldnt/matter", nil) + rsp := httptest.NewRecorder() + handler.ServeHTTP(rsp, req) + + require.Equal(t, test.wantStatus, rsp.Code) + + if test.wantContentType != "" { + require.Equal(t, test.wantContentType, rsp.Header().Get("Content-Type")) + } + + if test.wantBody != nil { + wantJSON, err := json.Marshal(test.wantBody) + require.NoError(t, err) + require.JSONEq(t, string(wantJSON), rsp.Body.String()) + } + }) + } +} diff --git a/internal/oidc/issuerprovider/issuerprovider.go b/internal/oidc/issuerprovider/issuerprovider.go new file mode 100644 index 000000000..248258601 --- /dev/null +++ b/internal/oidc/issuerprovider/issuerprovider.go @@ -0,0 +1,32 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package issuerprovider provides a thread-safe type that can hold on to an OIDC issuer name. +package issuerprovider + +import "sync" + +// Provider is a type that can hold onto an issuer value, which may be nil. +// +// It is thread-safe. +type Provider struct { + mu sync.RWMutex + issuer *string +} + +// New returns an empty Provider, i.e., one that holds a nil issuer. +func New() *Provider { + return &Provider{} +} + +func (p *Provider) SetIssuer(issuer *string) { + p.mu.Lock() + defer p.mu.Unlock() + p.issuer = issuer +} + +func (p *Provider) GetIssuer() *string { + p.mu.RLock() + defer p.mu.RUnlock() + return p.issuer +} diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go new file mode 100644 index 000000000..795c3d380 --- /dev/null +++ b/internal/oidc/oidc.go @@ -0,0 +1,9 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package oidc contains common OIDC functionality needed by Pinniped. +package oidc + +const ( + WellKnownURLPath = "/.well-known/openid-configuration" +) From 006d96ab927362361779e940f814858d324453be Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Tue, 6 Oct 2020 10:12:29 -0400 Subject: [PATCH 03/28] supervisor-oidc: create dynamic config in YTT templates Signed-off-by: Andrew Keesler --- deploy-supervisor/deployment.yaml | 23 +++++++++++++++++++++++ deploy-supervisor/values.yaml | 6 ++++++ hack/prepare-for-integration-tests.sh | 6 +++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/deploy-supervisor/deployment.yaml b/deploy-supervisor/deployment.yaml index cd4079c8a..bb2f09564 100644 --- a/deploy-supervisor/deployment.yaml +++ b/deploy-supervisor/deployment.yaml @@ -30,6 +30,29 @@ data: names: dynamicConfigMap: (@= data.values.app_name + "-dynamic-config" @) --- +apiVersion: v1 +kind: ConfigMap +metadata: + name: #@ data.values.app_name + "-dynamic-config" + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +data: + issuer: #@ data.values.issuer_url +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: #@ data.values.app_name + "-static-config" + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +data: + #@yaml/text-templated-strings + pinniped.yaml: | + names: + dynamicConfigMap: (@= data.values.app_name + "-dynamic-config" @) +--- #@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "": apiVersion: v1 kind: Secret diff --git a/deploy-supervisor/values.yaml b/deploy-supervisor/values.yaml index 6df6efe99..ec0430a95 100644 --- a/deploy-supervisor/values.yaml +++ b/deploy-supervisor/values.yaml @@ -20,3 +20,9 @@ image_tag: latest #! Typically the value would be the output of: kubectl create secret docker-registry x --docker-server=https://example.io --docker-username="USERNAME" --docker-password="PASSWORD" --dry-run=client -o json | jq -r '.data[".dockerconfigjson"]' #! Optional. image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}} + +#! Specifies the base URL used in the endpoint fields (e.g., authorization_endpoint, jwks_url, etc.) +#! of the OpenID Provider Metadata, as well as the value of the iss JWT claim that will be used by +#! this OIDC provider. Per the OIDC Discovery spec, this URL must use the HTTPS scheme. See +#! https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3. +issuer_url: #! e.g., https://auth.my-org.com diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index 05f942881..f5d5073f9 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -177,12 +177,16 @@ kubectl create secret generic "$test_username" \ # # Deploy the Pinniped Supervisor # +issuer_url=https://todo.what-should-this-be + pushd deploy-supervisor >/dev/null log_note "Deploying the Pinniped Supervisor app to the cluster..." ytt --file . \ --data-value "image_repo=$registry_repo" \ - --data-value "image_tag=$tag" >"$manifest" + --data-value "image_tag=$tag" \ + --data-value "issuer_url=$issuer_url" \ + >"$manifest" kapp deploy --yes --app "pinniped-supervisor" --diff-changes --file "$manifest" From 5b3dd5fc7d02cce28d3113e451b2364942e1444b Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 6 Oct 2020 14:59:03 -0400 Subject: [PATCH 04/28] Rename pinniped-server -> pinniped-concierge Do we like this? We don't know yet. Signed-off-by: Andrew Keesler --- Dockerfile | 6 +++--- .../main.go | 2 +- internal/{ => concierge}/apiserver/apiserver.go | 2 +- internal/{ => concierge}/server/server.go | 10 +++++----- internal/{ => concierge}/server/server_test.go | 10 +++++----- .../{ => concierge}/server/testdata/podinfo/labels | 0 .../server/testdata/podinfo/namespace | 0 .../server/testdata/valid-config.yaml | 0 test/integration/cli_test.go | 8 ++++---- ...ery_test.go => concierge_api_discovery_test.go} | 0 ...test.go => concierge_api_serving_certs_test.go} | 0 ...lity_test.go => concierge_availability_test.go} | 0 ...go => concierge_credentialissuerconfig_test.go} | 0 ...test.go => concierge_credentialrequest_test.go} | 4 ++-- ...ent_test.go => concierge_kubecertagent_test.go} | 0 .../{kubectl_test.go => concierge_kubectl_test.go} | 0 .../common_test.go => library/access.go} | 14 +++++++------- 17 files changed, 28 insertions(+), 28 deletions(-) rename cmd/{pinniped-server => pinniped-concierge}/main.go (92%) rename internal/{ => concierge}/apiserver/apiserver.go (95%) rename internal/{ => concierge}/server/server.go (95%) rename internal/{ => concierge}/server/server_test.go (87%) rename internal/{ => concierge}/server/testdata/podinfo/labels (100%) rename internal/{ => concierge}/server/testdata/podinfo/namespace (100%) rename internal/{ => concierge}/server/testdata/valid-config.yaml (100%) rename test/integration/{api_discovery_test.go => concierge_api_discovery_test.go} (100%) rename test/integration/{api_serving_certs_test.go => concierge_api_serving_certs_test.go} (100%) rename test/integration/{app_availability_test.go => concierge_availability_test.go} (100%) rename test/integration/{credentialissuerconfig_test.go => concierge_credentialissuerconfig_test.go} (100%) rename test/integration/{credentialrequest_test.go => concierge_credentialrequest_test.go} (96%) rename test/integration/{kubecertagent_test.go => concierge_kubecertagent_test.go} (100%) rename test/integration/{kubectl_test.go => concierge_kubectl_test.go} (100%) rename test/{integration/common_test.go => library/access.go} (98%) diff --git a/Dockerfile b/Dockerfile index b5dc23a1f..be8fb1703 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ COPY hack ./hack # Build the executable binary (CGO_ENABLED=0 means static linking) RUN mkdir out \ - && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(hack/get-ldflags.sh)" -o out ./cmd/pinniped-server/... \ + && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(hack/get-ldflags.sh)" -o out ./cmd/pinniped-concierge/... \ && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(hack/get-ldflags.sh)" -o out ./cmd/pinniped-supervisor/... \ && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o out ./cmd/local-user-authenticator/... @@ -28,7 +28,7 @@ RUN mkdir out \ FROM debian:10.5-slim # Copy the binaries from the build-env stage -COPY --from=build-env /work/out/pinniped-server /usr/local/bin/pinniped-server +COPY --from=build-env /work/out/pinniped-concierge /usr/local/bin/pinniped-concierge COPY --from=build-env /work/out/pinniped-supervisor /usr/local/bin/pinniped-supervisor COPY --from=build-env /work/out/local-user-authenticator /usr/local/bin/local-user-authenticator @@ -36,4 +36,4 @@ COPY --from=build-env /work/out/local-user-authenticator /usr/local/bin/local-us EXPOSE 443 # Set the entrypoint -ENTRYPOINT ["/usr/local/bin/pinniped-server"] +ENTRYPOINT ["/usr/local/bin/pinniped-concierge"] diff --git a/cmd/pinniped-server/main.go b/cmd/pinniped-concierge/main.go similarity index 92% rename from cmd/pinniped-server/main.go rename to cmd/pinniped-concierge/main.go index 5741a696d..8ca7bf446 100644 --- a/cmd/pinniped-server/main.go +++ b/cmd/pinniped-concierge/main.go @@ -12,7 +12,7 @@ import ( "k8s.io/component-base/logs" "k8s.io/klog/v2" - "go.pinniped.dev/internal/server" + "go.pinniped.dev/internal/concierge/server" ) func main() { diff --git a/internal/apiserver/apiserver.go b/internal/concierge/apiserver/apiserver.go similarity index 95% rename from internal/apiserver/apiserver.go rename to internal/concierge/apiserver/apiserver.go index 98d2c7bd0..5dbcd9b1a 100644 --- a/internal/apiserver/apiserver.go +++ b/internal/concierge/apiserver/apiserver.go @@ -87,7 +87,7 @@ func (c *Config) Complete() CompletedConfig { // New returns a new instance of AdmissionServer from the given config. func (c completedConfig) New() (*PinnipedServer, error) { - genericServer, err := c.GenericConfig.New("pinniped-server", genericapiserver.NewEmptyDelegate()) // completion is done in Complete, no need for a second time + genericServer, err := c.GenericConfig.New("pinniped-concierge", genericapiserver.NewEmptyDelegate()) // completion is done in Complete, no need for a second time if err != nil { return nil, fmt.Errorf("completion error: %w", err) } diff --git a/internal/server/server.go b/internal/concierge/server/server.go similarity index 95% rename from internal/server/server.go rename to internal/concierge/server/server.go index 63456a794..23ad12582 100644 --- a/internal/server/server.go +++ b/internal/concierge/server/server.go @@ -1,7 +1,7 @@ // Copyright 2020 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// Package server is the command line entry point for pinniped-server. +// Package server is the command line entry point for pinniped-concierge. package server import ( @@ -15,8 +15,8 @@ import ( genericoptions "k8s.io/apiserver/pkg/server/options" loginv1alpha1 "go.pinniped.dev/generated/1.19/apis/login/v1alpha1" - "go.pinniped.dev/internal/apiserver" "go.pinniped.dev/internal/certauthority/dynamiccertauthority" + "go.pinniped.dev/internal/concierge/apiserver" "go.pinniped.dev/internal/controller/identityprovider/idpcache" "go.pinniped.dev/internal/controllermanager" "go.pinniped.dev/internal/downward" @@ -26,7 +26,7 @@ import ( "go.pinniped.dev/pkg/config" ) -// App is an object that represents the pinniped-server application. +// App is an object that represents the pinniped-concierge application. type App struct { cmd *cobra.Command @@ -54,9 +54,9 @@ func (a *App) Run() error { // Create the server command and save it into the App. func (a *App) addServerCommand(ctx context.Context, args []string, stdout, stderr io.Writer) { cmd := &cobra.Command{ - Use: "pinniped-server", + Use: "pinniped-concierge", Long: here.Doc(` - pinniped-server provides a generic API for mapping an external + pinniped-concierge provides a generic API for mapping an external credential from somewhere to an internal credential to be used for authenticating to the Kubernetes API.`), RunE: func(cmd *cobra.Command, args []string) error { return a.runServer(ctx) }, diff --git a/internal/server/server_test.go b/internal/concierge/server/server_test.go similarity index 87% rename from internal/server/server_test.go rename to internal/concierge/server/server_test.go index c5911c353..790e493e4 100644 --- a/internal/server/server_test.go +++ b/internal/concierge/server/server_test.go @@ -15,17 +15,17 @@ import ( ) const knownGoodUsage = ` -pinniped-server provides a generic API for mapping an external +pinniped-concierge provides a generic API for mapping an external credential from somewhere to an internal credential to be used for authenticating to the Kubernetes API. Usage: - pinniped-server [flags] + pinniped-concierge [flags] Flags: -c, --config string path to configuration file (default "pinniped.yaml") --downward-api-path string path to Downward API volume mount (default "/etc/podinfo") - -h, --help help for pinniped-server + -h, --help help for pinniped-concierge --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) ` @@ -48,7 +48,7 @@ func TestCommand(t *testing.T) { { name: "OneArgFails", args: []string{"tuna"}, - wantErr: `unknown command "tuna" for "pinniped-server"`, + wantErr: `unknown command "tuna" for "pinniped-concierge"`, }, { name: "ShortConfigFlagSucceeds", @@ -64,7 +64,7 @@ func TestCommand(t *testing.T) { "--config", "some/path/to/config.yaml", "tuna", }, - wantErr: `unknown command "tuna" for "pinniped-server"`, + wantErr: `unknown command "tuna" for "pinniped-concierge"`, }, } for _, test := range tests { diff --git a/internal/server/testdata/podinfo/labels b/internal/concierge/server/testdata/podinfo/labels similarity index 100% rename from internal/server/testdata/podinfo/labels rename to internal/concierge/server/testdata/podinfo/labels diff --git a/internal/server/testdata/podinfo/namespace b/internal/concierge/server/testdata/podinfo/namespace similarity index 100% rename from internal/server/testdata/podinfo/namespace rename to internal/concierge/server/testdata/podinfo/namespace diff --git a/internal/server/testdata/valid-config.yaml b/internal/concierge/server/testdata/valid-config.yaml similarity index 100% rename from internal/server/testdata/valid-config.yaml rename to internal/concierge/server/testdata/valid-config.yaml diff --git a/test/integration/cli_test.go b/test/integration/cli_test.go index 731f97fe5..1e3372620 100644 --- a/test/integration/cli_test.go +++ b/test/integration/cli_test.go @@ -62,13 +62,13 @@ func TestCLI(t *testing.T) { adminClient := library.NewClientset(t) t.Run( "access as user with kubectl", - accessAsUserWithKubectlTest(ctx, adminClient, kubeConfigYAML, env.TestUser.ExpectedUsername, env.Namespace), + library.AccessAsUserWithKubectlTest(ctx, adminClient, kubeConfigYAML, env.TestUser.ExpectedUsername, env.Namespace), ) for _, group := range env.TestUser.ExpectedGroups { group := group t.Run( "access as group "+group+" with kubectl", - accessAsGroupWithKubectlTest(ctx, adminClient, kubeConfigYAML, group, env.Namespace), + library.AccessAsGroupWithKubectlTest(ctx, adminClient, kubeConfigYAML, group, env.Namespace), ) } @@ -76,10 +76,10 @@ func TestCLI(t *testing.T) { kubeClient := library.NewClientsetForKubeConfig(t, kubeConfigYAML) // Validate that we can auth to the API via our user. - t.Run("access as user with client-go", accessAsUserTest(ctx, adminClient, env.TestUser.ExpectedUsername, kubeClient)) + t.Run("access as user with client-go", library.AccessAsUserTest(ctx, adminClient, env.TestUser.ExpectedUsername, kubeClient)) for _, group := range env.TestUser.ExpectedGroups { group := group - t.Run("access as group "+group+" with client-go", accessAsGroupTest(ctx, adminClient, group, kubeClient)) + t.Run("access as group "+group+" with client-go", library.AccessAsGroupTest(ctx, adminClient, group, kubeClient)) } } diff --git a/test/integration/api_discovery_test.go b/test/integration/concierge_api_discovery_test.go similarity index 100% rename from test/integration/api_discovery_test.go rename to test/integration/concierge_api_discovery_test.go diff --git a/test/integration/api_serving_certs_test.go b/test/integration/concierge_api_serving_certs_test.go similarity index 100% rename from test/integration/api_serving_certs_test.go rename to test/integration/concierge_api_serving_certs_test.go diff --git a/test/integration/app_availability_test.go b/test/integration/concierge_availability_test.go similarity index 100% rename from test/integration/app_availability_test.go rename to test/integration/concierge_availability_test.go diff --git a/test/integration/credentialissuerconfig_test.go b/test/integration/concierge_credentialissuerconfig_test.go similarity index 100% rename from test/integration/credentialissuerconfig_test.go rename to test/integration/concierge_credentialissuerconfig_test.go diff --git a/test/integration/credentialrequest_test.go b/test/integration/concierge_credentialrequest_test.go similarity index 96% rename from test/integration/credentialrequest_test.go rename to test/integration/concierge_credentialrequest_test.go index 277e7f3d5..75b95719f 100644 --- a/test/integration/credentialrequest_test.go +++ b/test/integration/concierge_credentialrequest_test.go @@ -77,13 +77,13 @@ func TestSuccessfulCredentialRequest(t *testing.T) { t.Run( "access as user", - accessAsUserTest(ctx, adminClient, env.TestUser.ExpectedUsername, clientWithCertFromCredentialRequest), + library.AccessAsUserTest(ctx, adminClient, env.TestUser.ExpectedUsername, clientWithCertFromCredentialRequest), ) for _, group := range env.TestUser.ExpectedGroups { group := group t.Run( "access as group "+group, - accessAsGroupTest(ctx, adminClient, group, clientWithCertFromCredentialRequest), + library.AccessAsGroupTest(ctx, adminClient, group, clientWithCertFromCredentialRequest), ) } } diff --git a/test/integration/kubecertagent_test.go b/test/integration/concierge_kubecertagent_test.go similarity index 100% rename from test/integration/kubecertagent_test.go rename to test/integration/concierge_kubecertagent_test.go diff --git a/test/integration/kubectl_test.go b/test/integration/concierge_kubectl_test.go similarity index 100% rename from test/integration/kubectl_test.go rename to test/integration/concierge_kubectl_test.go diff --git a/test/integration/common_test.go b/test/library/access.go similarity index 98% rename from test/integration/common_test.go rename to test/library/access.go index 38528fca9..89b74704d 100644 --- a/test/integration/common_test.go +++ b/test/library/access.go @@ -1,6 +1,7 @@ // Copyright 2020 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package integration + +package library import ( "context" @@ -11,12 +12,11 @@ import ( "testing" "time" - "k8s.io/apimachinery/pkg/api/errors" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -31,7 +31,7 @@ const ( // // Use this function if you want to simply validate that a user can auth to the kube API after // performing a Pinniped credential exchange. -func accessAsUserTest( +func AccessAsUserTest( ctx context.Context, adminClient kubernetes.Interface, testUsername string, @@ -53,7 +53,7 @@ func accessAsUserTest( } } -func accessAsUserWithKubectlTest( +func AccessAsUserWithKubectlTest( ctx context.Context, adminClient kubernetes.Interface, testKubeConfigYAML string, @@ -82,7 +82,7 @@ func accessAsUserWithKubectlTest( // // Use this function if you want to simply validate that a user can auth to the kube API (via // a group membership) after performing a Pinniped credential exchange. -func accessAsGroupTest( +func AccessAsGroupTest( ctx context.Context, adminClient kubernetes.Interface, testGroup string, @@ -104,7 +104,7 @@ func accessAsGroupTest( } } -func accessAsGroupWithKubectlTest( +func AccessAsGroupWithKubectlTest( ctx context.Context, adminClient kubernetes.Interface, testKubeConfigYAML string, From 14f1d868333a6f9b57cc3f14c409bff37bd3da69 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 6 Oct 2020 15:20:29 -0400 Subject: [PATCH 05/28] supervisor-oidc: add OIDCProviderConfig CRD This will hopefully come in handy later if we ever decide to add support for multiple OIDC providers as a part of one supervisor. Signed-off-by: Andrew Keesler --- ...l => types_credentialissuerconfig.go.tmpl} | 0 .../v1alpha1/types_oidcproviderconfig.go.tmpl | 42 +++++ ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 65 +++++++ generated/1.17/README.adoc | 39 +++++ ...pes.go => types_credentialissuerconfig.go} | 0 .../v1alpha1/types_oidcproviderconfig.go | 42 +++++ .../config/v1alpha1/zz_generated.deepcopy.go | 76 ++++++++ .../typed/config/v1alpha1/config_client.go | 5 + .../v1alpha1/fake/fake_config_client.go | 4 + .../v1alpha1/fake/fake_oidcproviderconfig.go | 115 ++++++++++++ .../config/v1alpha1/generated_expansion.go | 2 + .../config/v1alpha1/oidcproviderconfig.go | 161 +++++++++++++++++ .../config/v1alpha1/interface.go | 7 + .../config/v1alpha1/oidcproviderconfig.go | 76 ++++++++ .../informers/externalversions/generic.go | 2 + .../config/v1alpha1/expansion_generated.go | 8 + .../config/v1alpha1/oidcproviderconfig.go | 81 +++++++++ .../client/openapi/zz_generated.openapi.go | 111 ++++++++++++ ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 65 +++++++ generated/1.18/README.adoc | 39 +++++ ...pes.go => types_credentialissuerconfig.go} | 0 .../v1alpha1/types_oidcproviderconfig.go | 42 +++++ .../config/v1alpha1/zz_generated.deepcopy.go | 76 ++++++++ .../typed/config/v1alpha1/config_client.go | 5 + .../v1alpha1/fake/fake_config_client.go | 4 + .../v1alpha1/fake/fake_oidcproviderconfig.go | 117 +++++++++++++ .../config/v1alpha1/generated_expansion.go | 2 + .../config/v1alpha1/oidcproviderconfig.go | 165 ++++++++++++++++++ .../config/v1alpha1/interface.go | 7 + .../config/v1alpha1/oidcproviderconfig.go | 77 ++++++++ .../informers/externalversions/generic.go | 2 + .../config/v1alpha1/expansion_generated.go | 8 + .../config/v1alpha1/oidcproviderconfig.go | 81 +++++++++ .../client/openapi/zz_generated.openapi.go | 111 ++++++++++++ ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 65 +++++++ generated/1.19/README.adoc | 39 +++++ ...pes.go => types_credentialissuerconfig.go} | 0 .../v1alpha1/types_oidcproviderconfig.go | 42 +++++ .../config/v1alpha1/zz_generated.deepcopy.go | 76 ++++++++ .../typed/config/v1alpha1/config_client.go | 5 + .../v1alpha1/fake/fake_config_client.go | 4 + .../v1alpha1/fake/fake_oidcproviderconfig.go | 117 +++++++++++++ .../config/v1alpha1/generated_expansion.go | 2 + .../config/v1alpha1/oidcproviderconfig.go | 165 ++++++++++++++++++ .../config/v1alpha1/interface.go | 7 + .../config/v1alpha1/oidcproviderconfig.go | 77 ++++++++ .../informers/externalversions/generic.go | 2 + .../config/v1alpha1/expansion_generated.go | 8 + .../config/v1alpha1/oidcproviderconfig.go | 86 +++++++++ .../client/openapi/zz_generated.openapi.go | 111 ++++++++++++ ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 65 +++++++ test/integration/supervisor_discovery_test.go | 37 ++++ 52 files changed, 2545 insertions(+) rename apis/config/v1alpha1/{types.go.tmpl => types_credentialissuerconfig.go.tmpl} (100%) create mode 100644 apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl create mode 100644 deploy/config.pinniped.dev_oidcproviderconfigs.yaml rename generated/1.17/apis/config/v1alpha1/{types.go => types_credentialissuerconfig.go} (100%) create mode 100644 generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go create mode 100644 generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go create mode 100644 generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.17/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.17/client/listers/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml rename generated/1.18/apis/config/v1alpha1/{types.go => types_credentialissuerconfig.go} (100%) create mode 100644 generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go create mode 100644 generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go create mode 100644 generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.18/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.18/client/listers/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml rename generated/1.19/apis/config/v1alpha1/{types.go => types_credentialissuerconfig.go} (100%) create mode 100644 generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go create mode 100644 generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go create mode 100644 generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.19/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.19/client/listers/config/v1alpha1/oidcproviderconfig.go create mode 100644 generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml create mode 100644 test/integration/supervisor_discovery_test.go diff --git a/apis/config/v1alpha1/types.go.tmpl b/apis/config/v1alpha1/types_credentialissuerconfig.go.tmpl similarity index 100% rename from apis/config/v1alpha1/types.go.tmpl rename to apis/config/v1alpha1/types_credentialissuerconfig.go.tmpl diff --git a/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl new file mode 100644 index 000000000..7000aead5 --- /dev/null +++ b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl @@ -0,0 +1,42 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// OIDCProviderConfigSpec is a struct that describes an OIDC Provider. +type OIDCProviderConfigSpec struct { + // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the + // identifier that it will use for the iss claim in issued JWTs. This field will also be used as + // the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is + // https://example.com/foo, then your authorization endpoint will look like + // https://example.com/foo/some/path/to/auth/endpoint). + // + // See + // https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. + // +kubebuilder:validation:MinLength=1 + Issuer string `json:"issuer"` +} + +// OIDCProviderConfig describes the configuration of an OIDC provider. +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=opc +type OIDCProviderConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec of the OIDC provider. + Spec OIDCProviderConfigSpec `json:"status"` +} + +// List of OIDCProviderConfig objects. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type OIDCProviderConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []OIDCProviderConfig `json:"items"` +} diff --git a/deploy/config.pinniped.dev_oidcproviderconfigs.yaml b/deploy/config.pinniped.dev_oidcproviderconfigs.yaml new file mode 100644 index 000000000..961da2538 --- /dev/null +++ b/deploy/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,65 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.0 + creationTimestamp: null + name: oidcproviderconfigs.config.pinniped.dev +spec: + group: config.pinniped.dev + names: + kind: OIDCProviderConfig + listKind: OIDCProviderConfigList + plural: oidcproviderconfigs + shortNames: + - opc + singular: oidcproviderconfig + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: OIDCProviderConfig describes the configuration of an OIDC provider. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + status: + description: Spec of the OIDC provider. + properties: + issuer: + description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery + Metadata document, as well as the identifier that it will use for + the iss claim in issued JWTs. This field will also be used as the + base URL for any endpoints used by the OIDC Provider (e.g., if your + issuer is https://example.com/foo, then your authorization endpoint + will look like https://example.com/foo/some/path/to/auth/endpoint). + \n See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 + for more information." + minLength: 1 + type: string + required: + - issuer + type: object + required: + - status + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/generated/1.17/README.adoc b/generated/1.17/README.adoc index 0c2458dfa..1953af028 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -95,6 +95,45 @@ Status of a credential issuer. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfig"] +==== OIDCProviderConfig + +OIDCProviderConfig describes the configuration of an OIDC provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfiglist[$$OIDCProviderConfigList$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. + +| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +|=== + + + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfigspec"] +==== OIDCProviderConfigSpec + +OIDCProviderConfigSpec is a struct that describes an OIDC Provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfig[$$OIDCProviderConfig$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`issuer`* __string__ | Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the identifier that it will use for the iss claim in issued JWTs. This field will also be used as the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is https://example.com/foo, then your authorization endpoint will look like https://example.com/foo/some/path/to/auth/endpoint). + See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 diff --git a/generated/1.17/apis/config/v1alpha1/types.go b/generated/1.17/apis/config/v1alpha1/types_credentialissuerconfig.go similarity index 100% rename from generated/1.17/apis/config/v1alpha1/types.go rename to generated/1.17/apis/config/v1alpha1/types_credentialissuerconfig.go diff --git a/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go new file mode 100644 index 000000000..7000aead5 --- /dev/null +++ b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -0,0 +1,42 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// OIDCProviderConfigSpec is a struct that describes an OIDC Provider. +type OIDCProviderConfigSpec struct { + // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the + // identifier that it will use for the iss claim in issued JWTs. This field will also be used as + // the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is + // https://example.com/foo, then your authorization endpoint will look like + // https://example.com/foo/some/path/to/auth/endpoint). + // + // See + // https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. + // +kubebuilder:validation:MinLength=1 + Issuer string `json:"issuer"` +} + +// OIDCProviderConfig describes the configuration of an OIDC provider. +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=opc +type OIDCProviderConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec of the OIDC provider. + Spec OIDCProviderConfigSpec `json:"status"` +} + +// List of OIDCProviderConfig objects. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type OIDCProviderConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []OIDCProviderConfig `json:"items"` +} diff --git a/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go index 4e72d35da..f45d80c35 100644 --- a/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -131,3 +131,79 @@ func (in *CredentialIssuerConfigStrategy) DeepCopy() *CredentialIssuerConfigStra in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfig. +func (in *OIDCProviderConfig) DeepCopy() *OIDCProviderConfig { + if in == nil { + return nil + } + out := new(OIDCProviderConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OIDCProviderConfig) 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 *OIDCProviderConfigList) DeepCopyInto(out *OIDCProviderConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OIDCProviderConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigList. +func (in *OIDCProviderConfigList) DeepCopy() *OIDCProviderConfigList { + if in == nil { + return nil + } + out := new(OIDCProviderConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OIDCProviderConfigList) 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 *OIDCProviderConfigSpec) DeepCopyInto(out *OIDCProviderConfigSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigSpec. +func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { + if in == nil { + return nil + } + out := new(OIDCProviderConfigSpec) + in.DeepCopyInto(out) + return out +} diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/config_client.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/config_client.go index 09c089255..c678bbfe9 100644 --- a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/config_client.go +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/config_client.go @@ -14,6 +14,7 @@ import ( type ConfigV1alpha1Interface interface { RESTClient() rest.Interface CredentialIssuerConfigsGetter + OIDCProviderConfigsGetter } // ConfigV1alpha1Client is used to interact with features provided by the config.pinniped.dev group. @@ -25,6 +26,10 @@ func (c *ConfigV1alpha1Client) CredentialIssuerConfigs(namespace string) Credent return newCredentialIssuerConfigs(c, namespace) } +func (c *ConfigV1alpha1Client) OIDCProviderConfigs(namespace string) OIDCProviderConfigInterface { + return newOIDCProviderConfigs(c, namespace) +} + // NewForConfig creates a new ConfigV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*ConfigV1alpha1Client, error) { config := *c diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go index 38be69416..b475f6c68 100644 --- a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go @@ -19,6 +19,10 @@ func (c *FakeConfigV1alpha1) CredentialIssuerConfigs(namespace string) v1alpha1. return &FakeCredentialIssuerConfigs{c, namespace} } +func (c *FakeConfigV1alpha1) OIDCProviderConfigs(namespace string) v1alpha1.OIDCProviderConfigInterface { + return &FakeOIDCProviderConfigs{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeConfigV1alpha1) RESTClient() rest.Interface { diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go new file mode 100644 index 000000000..6cd7dc42b --- /dev/null +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -0,0 +1,115 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "go.pinniped.dev/generated/1.17/apis/config/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeOIDCProviderConfigs implements OIDCProviderConfigInterface +type FakeOIDCProviderConfigs struct { + Fake *FakeConfigV1alpha1 + ns string +} + +var oidcproviderconfigsResource = schema.GroupVersionResource{Group: "config.pinniped.dev", Version: "v1alpha1", Resource: "oidcproviderconfigs"} + +var oidcproviderconfigsKind = schema.GroupVersionKind{Group: "config.pinniped.dev", Version: "v1alpha1", Kind: "OIDCProviderConfig"} + +// Get takes name of the oIDCProviderConfig, and returns the corresponding oIDCProviderConfig object, and an error if there is any. +func (c *FakeOIDCProviderConfigs) Get(name string, options v1.GetOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(oidcproviderconfigsResource, c.ns, name), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// List takes label and field selectors, and returns the list of OIDCProviderConfigs that match those selectors. +func (c *FakeOIDCProviderConfigs) List(opts v1.ListOptions) (result *v1alpha1.OIDCProviderConfigList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(oidcproviderconfigsResource, oidcproviderconfigsKind, c.ns, opts), &v1alpha1.OIDCProviderConfigList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.OIDCProviderConfigList{ListMeta: obj.(*v1alpha1.OIDCProviderConfigList).ListMeta} + for _, item := range obj.(*v1alpha1.OIDCProviderConfigList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested oIDCProviderConfigs. +func (c *FakeOIDCProviderConfigs) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(oidcproviderconfigsResource, c.ns, opts)) + +} + +// Create takes the representation of a oIDCProviderConfig and creates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *FakeOIDCProviderConfigs) Create(oIDCProviderConfig *v1alpha1.OIDCProviderConfig) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(oidcproviderconfigsResource, c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// Update takes the representation of a oIDCProviderConfig and updates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *FakeOIDCProviderConfigs) Update(oIDCProviderConfig *v1alpha1.OIDCProviderConfig) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(oidcproviderconfigsResource, c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. +func (c *FakeOIDCProviderConfigs) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(oidcproviderconfigsResource, c.ns, name), &v1alpha1.OIDCProviderConfig{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeOIDCProviderConfigs) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(oidcproviderconfigsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.OIDCProviderConfigList{}) + return err +} + +// Patch applies the patch and returns the patched oIDCProviderConfig. +func (c *FakeOIDCProviderConfigs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(oidcproviderconfigsResource, c.ns, name, pt, data, subresources...), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go index 61507202b..5da5f449c 100644 --- a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go @@ -6,3 +6,5 @@ package v1alpha1 type CredentialIssuerConfigExpansion interface{} + +type OIDCProviderConfigExpansion interface{} diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..23f8760fd --- /dev/null +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,161 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "go.pinniped.dev/generated/1.17/apis/config/v1alpha1" + scheme "go.pinniped.dev/generated/1.17/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// OIDCProviderConfigsGetter has a method to return a OIDCProviderConfigInterface. +// A group's client should implement this interface. +type OIDCProviderConfigsGetter interface { + OIDCProviderConfigs(namespace string) OIDCProviderConfigInterface +} + +// OIDCProviderConfigInterface has methods to work with OIDCProviderConfig resources. +type OIDCProviderConfigInterface interface { + Create(*v1alpha1.OIDCProviderConfig) (*v1alpha1.OIDCProviderConfig, error) + Update(*v1alpha1.OIDCProviderConfig) (*v1alpha1.OIDCProviderConfig, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.OIDCProviderConfig, error) + List(opts v1.ListOptions) (*v1alpha1.OIDCProviderConfigList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) + OIDCProviderConfigExpansion +} + +// oIDCProviderConfigs implements OIDCProviderConfigInterface +type oIDCProviderConfigs struct { + client rest.Interface + ns string +} + +// newOIDCProviderConfigs returns a OIDCProviderConfigs +func newOIDCProviderConfigs(c *ConfigV1alpha1Client, namespace string) *oIDCProviderConfigs { + return &oIDCProviderConfigs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the oIDCProviderConfig, and returns the corresponding oIDCProviderConfig object, and an error if there is any. +func (c *oIDCProviderConfigs) Get(name string, options v1.GetOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of OIDCProviderConfigs that match those selectors. +func (c *oIDCProviderConfigs) List(opts v1.ListOptions) (result *v1alpha1.OIDCProviderConfigList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.OIDCProviderConfigList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested oIDCProviderConfigs. +func (c *oIDCProviderConfigs) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a oIDCProviderConfig and creates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *oIDCProviderConfigs) Create(oIDCProviderConfig *v1alpha1.OIDCProviderConfig) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Post(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Body(oIDCProviderConfig). + Do(). + Into(result) + return +} + +// Update takes the representation of a oIDCProviderConfig and updates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *oIDCProviderConfigs) Update(oIDCProviderConfig *v1alpha1.OIDCProviderConfig) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(oIDCProviderConfig.Name). + Body(oIDCProviderConfig). + Do(). + Into(result) + return +} + +// Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. +func (c *oIDCProviderConfigs) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *oIDCProviderConfigs) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched oIDCProviderConfig. +func (c *oIDCProviderConfigs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/generated/1.17/client/informers/externalversions/config/v1alpha1/interface.go b/generated/1.17/client/informers/externalversions/config/v1alpha1/interface.go index 1722f7473..7a1f98a1e 100644 --- a/generated/1.17/client/informers/externalversions/config/v1alpha1/interface.go +++ b/generated/1.17/client/informers/externalversions/config/v1alpha1/interface.go @@ -13,6 +13,8 @@ import ( type Interface interface { // CredentialIssuerConfigs returns a CredentialIssuerConfigInformer. CredentialIssuerConfigs() CredentialIssuerConfigInformer + // OIDCProviderConfigs returns a OIDCProviderConfigInformer. + OIDCProviderConfigs() OIDCProviderConfigInformer } type version struct { @@ -30,3 +32,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (v *version) CredentialIssuerConfigs() CredentialIssuerConfigInformer { return &credentialIssuerConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// OIDCProviderConfigs returns a OIDCProviderConfigInformer. +func (v *version) OIDCProviderConfigs() OIDCProviderConfigInformer { + return &oIDCProviderConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/generated/1.17/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go b/generated/1.17/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..68fa7cd61 --- /dev/null +++ b/generated/1.17/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,76 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + configv1alpha1 "go.pinniped.dev/generated/1.17/apis/config/v1alpha1" + versioned "go.pinniped.dev/generated/1.17/client/clientset/versioned" + internalinterfaces "go.pinniped.dev/generated/1.17/client/informers/externalversions/internalinterfaces" + v1alpha1 "go.pinniped.dev/generated/1.17/client/listers/config/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// OIDCProviderConfigInformer provides access to a shared informer and lister for +// OIDCProviderConfigs. +type OIDCProviderConfigInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.OIDCProviderConfigLister +} + +type oIDCProviderConfigInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewOIDCProviderConfigInformer constructs a new informer for OIDCProviderConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewOIDCProviderConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredOIDCProviderConfigInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredOIDCProviderConfigInformer constructs a new informer for OIDCProviderConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredOIDCProviderConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ConfigV1alpha1().OIDCProviderConfigs(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ConfigV1alpha1().OIDCProviderConfigs(namespace).Watch(options) + }, + }, + &configv1alpha1.OIDCProviderConfig{}, + resyncPeriod, + indexers, + ) +} + +func (f *oIDCProviderConfigInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredOIDCProviderConfigInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *oIDCProviderConfigInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&configv1alpha1.OIDCProviderConfig{}, f.defaultInformer) +} + +func (f *oIDCProviderConfigInformer) Lister() v1alpha1.OIDCProviderConfigLister { + return v1alpha1.NewOIDCProviderConfigLister(f.Informer().GetIndexer()) +} diff --git a/generated/1.17/client/informers/externalversions/generic.go b/generated/1.17/client/informers/externalversions/generic.go index 01e9dfa33..c3d7c11fd 100644 --- a/generated/1.17/client/informers/externalversions/generic.go +++ b/generated/1.17/client/informers/externalversions/generic.go @@ -44,6 +44,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=config.pinniped.dev, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("credentialissuerconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().CredentialIssuerConfigs().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("oidcproviderconfigs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().OIDCProviderConfigs().Informer()}, nil // Group=idp.pinniped.dev, Version=v1alpha1 case idpv1alpha1.SchemeGroupVersion.WithResource("webhookidentityproviders"): diff --git a/generated/1.17/client/listers/config/v1alpha1/expansion_generated.go b/generated/1.17/client/listers/config/v1alpha1/expansion_generated.go index 68c725a96..7f0b5b9d5 100644 --- a/generated/1.17/client/listers/config/v1alpha1/expansion_generated.go +++ b/generated/1.17/client/listers/config/v1alpha1/expansion_generated.go @@ -12,3 +12,11 @@ type CredentialIssuerConfigListerExpansion interface{} // CredentialIssuerConfigNamespaceListerExpansion allows custom methods to be added to // CredentialIssuerConfigNamespaceLister. type CredentialIssuerConfigNamespaceListerExpansion interface{} + +// OIDCProviderConfigListerExpansion allows custom methods to be added to +// OIDCProviderConfigLister. +type OIDCProviderConfigListerExpansion interface{} + +// OIDCProviderConfigNamespaceListerExpansion allows custom methods to be added to +// OIDCProviderConfigNamespaceLister. +type OIDCProviderConfigNamespaceListerExpansion interface{} diff --git a/generated/1.17/client/listers/config/v1alpha1/oidcproviderconfig.go b/generated/1.17/client/listers/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..5a6f8faa0 --- /dev/null +++ b/generated/1.17/client/listers/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,81 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "go.pinniped.dev/generated/1.17/apis/config/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// OIDCProviderConfigLister helps list OIDCProviderConfigs. +type OIDCProviderConfigLister interface { + // List lists all OIDCProviderConfigs in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) + // OIDCProviderConfigs returns an object that can list and get OIDCProviderConfigs. + OIDCProviderConfigs(namespace string) OIDCProviderConfigNamespaceLister + OIDCProviderConfigListerExpansion +} + +// oIDCProviderConfigLister implements the OIDCProviderConfigLister interface. +type oIDCProviderConfigLister struct { + indexer cache.Indexer +} + +// NewOIDCProviderConfigLister returns a new OIDCProviderConfigLister. +func NewOIDCProviderConfigLister(indexer cache.Indexer) OIDCProviderConfigLister { + return &oIDCProviderConfigLister{indexer: indexer} +} + +// List lists all OIDCProviderConfigs in the indexer. +func (s *oIDCProviderConfigLister) List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.OIDCProviderConfig)) + }) + return ret, err +} + +// OIDCProviderConfigs returns an object that can list and get OIDCProviderConfigs. +func (s *oIDCProviderConfigLister) OIDCProviderConfigs(namespace string) OIDCProviderConfigNamespaceLister { + return oIDCProviderConfigNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// OIDCProviderConfigNamespaceLister helps list and get OIDCProviderConfigs. +type OIDCProviderConfigNamespaceLister interface { + // List lists all OIDCProviderConfigs in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) + // Get retrieves the OIDCProviderConfig from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.OIDCProviderConfig, error) + OIDCProviderConfigNamespaceListerExpansion +} + +// oIDCProviderConfigNamespaceLister implements the OIDCProviderConfigNamespaceLister +// interface. +type oIDCProviderConfigNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all OIDCProviderConfigs in the indexer for a given namespace. +func (s oIDCProviderConfigNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.OIDCProviderConfig)) + }) + return ret, err +} + +// Get retrieves the OIDCProviderConfig from the indexer for a given namespace and name. +func (s oIDCProviderConfigNamespaceLister) Get(name string) (*v1alpha1.OIDCProviderConfig, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("oidcproviderconfig"), name) + } + return obj.(*v1alpha1.OIDCProviderConfig), nil +} diff --git a/generated/1.17/client/openapi/zz_generated.openapi.go b/generated/1.17/client/openapi/zz_generated.openapi.go index 83e7fb872..55aecb0a4 100644 --- a/generated/1.17/client/openapi/zz_generated.openapi.go +++ b/generated/1.17/client/openapi/zz_generated.openapi.go @@ -22,6 +22,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.CredentialIssuerConfigList": schema_117_apis_config_v1alpha1_CredentialIssuerConfigList(ref), "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.CredentialIssuerConfigStatus": schema_117_apis_config_v1alpha1_CredentialIssuerConfigStatus(ref), "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.CredentialIssuerConfigStrategy": schema_117_apis_config_v1alpha1_CredentialIssuerConfigStrategy(ref), + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfig": schema_117_apis_config_v1alpha1_OIDCProviderConfig(ref), + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigList": schema_117_apis_config_v1alpha1_OIDCProviderConfigList(ref), + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec": schema_117_apis_config_v1alpha1_OIDCProviderConfigSpec(ref), "go.pinniped.dev/generated/1.17/apis/idp/v1alpha1.Condition": schema_117_apis_idp_v1alpha1_Condition(ref), "go.pinniped.dev/generated/1.17/apis/idp/v1alpha1.TLSSpec": schema_117_apis_idp_v1alpha1_TLSSpec(ref), "go.pinniped.dev/generated/1.17/apis/idp/v1alpha1.WebhookIdentityProvider": schema_117_apis_idp_v1alpha1_WebhookIdentityProvider(ref), @@ -286,6 +289,114 @@ func schema_117_apis_config_v1alpha1_CredentialIssuerConfigStrategy(ref common.R } } +func schema_117_apis_config_v1alpha1_OIDCProviderConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfig describes the configuration of an OIDC provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Spec of the OIDC provider.", + Ref: ref("go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec"), + }, + }, + }, + Required: []string{"status"}, + }, + }, + Dependencies: []string{ + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_117_apis_config_v1alpha1_OIDCProviderConfigList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfig"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfig", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_117_apis_config_v1alpha1_OIDCProviderConfigSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfigSpec is a struct that describes an OIDC Provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "issuer": { + SchemaProps: spec.SchemaProps{ + Description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the identifier that it will use for the iss claim in issued JWTs. This field will also be used as the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is https://example.com/foo, then your authorization endpoint will look like https://example.com/foo/some/path/to/auth/endpoint).\n\nSee https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"issuer"}, + }, + }, + } +} + func schema_117_apis_idp_v1alpha1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml new file mode 100644 index 000000000..961da2538 --- /dev/null +++ b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,65 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.0 + creationTimestamp: null + name: oidcproviderconfigs.config.pinniped.dev +spec: + group: config.pinniped.dev + names: + kind: OIDCProviderConfig + listKind: OIDCProviderConfigList + plural: oidcproviderconfigs + shortNames: + - opc + singular: oidcproviderconfig + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: OIDCProviderConfig describes the configuration of an OIDC provider. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + status: + description: Spec of the OIDC provider. + properties: + issuer: + description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery + Metadata document, as well as the identifier that it will use for + the iss claim in issued JWTs. This field will also be used as the + base URL for any endpoints used by the OIDC Provider (e.g., if your + issuer is https://example.com/foo, then your authorization endpoint + will look like https://example.com/foo/some/path/to/auth/endpoint). + \n See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 + for more information." + minLength: 1 + type: string + required: + - issuer + type: object + required: + - status + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/generated/1.18/README.adoc b/generated/1.18/README.adoc index faf0ca70c..829768018 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -95,6 +95,45 @@ Status of a credential issuer. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfig"] +==== OIDCProviderConfig + +OIDCProviderConfig describes the configuration of an OIDC provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfiglist[$$OIDCProviderConfigList$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. + +| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +|=== + + + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfigspec"] +==== OIDCProviderConfigSpec + +OIDCProviderConfigSpec is a struct that describes an OIDC Provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfig[$$OIDCProviderConfig$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`issuer`* __string__ | Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the identifier that it will use for the iss claim in issued JWTs. This field will also be used as the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is https://example.com/foo, then your authorization endpoint will look like https://example.com/foo/some/path/to/auth/endpoint). + See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 diff --git a/generated/1.18/apis/config/v1alpha1/types.go b/generated/1.18/apis/config/v1alpha1/types_credentialissuerconfig.go similarity index 100% rename from generated/1.18/apis/config/v1alpha1/types.go rename to generated/1.18/apis/config/v1alpha1/types_credentialissuerconfig.go diff --git a/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go new file mode 100644 index 000000000..7000aead5 --- /dev/null +++ b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -0,0 +1,42 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// OIDCProviderConfigSpec is a struct that describes an OIDC Provider. +type OIDCProviderConfigSpec struct { + // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the + // identifier that it will use for the iss claim in issued JWTs. This field will also be used as + // the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is + // https://example.com/foo, then your authorization endpoint will look like + // https://example.com/foo/some/path/to/auth/endpoint). + // + // See + // https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. + // +kubebuilder:validation:MinLength=1 + Issuer string `json:"issuer"` +} + +// OIDCProviderConfig describes the configuration of an OIDC provider. +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=opc +type OIDCProviderConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec of the OIDC provider. + Spec OIDCProviderConfigSpec `json:"status"` +} + +// List of OIDCProviderConfig objects. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type OIDCProviderConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []OIDCProviderConfig `json:"items"` +} diff --git a/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go index 4e72d35da..f45d80c35 100644 --- a/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -131,3 +131,79 @@ func (in *CredentialIssuerConfigStrategy) DeepCopy() *CredentialIssuerConfigStra in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfig. +func (in *OIDCProviderConfig) DeepCopy() *OIDCProviderConfig { + if in == nil { + return nil + } + out := new(OIDCProviderConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OIDCProviderConfig) 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 *OIDCProviderConfigList) DeepCopyInto(out *OIDCProviderConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OIDCProviderConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigList. +func (in *OIDCProviderConfigList) DeepCopy() *OIDCProviderConfigList { + if in == nil { + return nil + } + out := new(OIDCProviderConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OIDCProviderConfigList) 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 *OIDCProviderConfigSpec) DeepCopyInto(out *OIDCProviderConfigSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigSpec. +func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { + if in == nil { + return nil + } + out := new(OIDCProviderConfigSpec) + in.DeepCopyInto(out) + return out +} diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/config_client.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/config_client.go index 29650a3bc..717ee6e9a 100644 --- a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/config_client.go +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/config_client.go @@ -14,6 +14,7 @@ import ( type ConfigV1alpha1Interface interface { RESTClient() rest.Interface CredentialIssuerConfigsGetter + OIDCProviderConfigsGetter } // ConfigV1alpha1Client is used to interact with features provided by the config.pinniped.dev group. @@ -25,6 +26,10 @@ func (c *ConfigV1alpha1Client) CredentialIssuerConfigs(namespace string) Credent return newCredentialIssuerConfigs(c, namespace) } +func (c *ConfigV1alpha1Client) OIDCProviderConfigs(namespace string) OIDCProviderConfigInterface { + return newOIDCProviderConfigs(c, namespace) +} + // NewForConfig creates a new ConfigV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*ConfigV1alpha1Client, error) { config := *c diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go index 665bfd360..dd2faeebb 100644 --- a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go @@ -19,6 +19,10 @@ func (c *FakeConfigV1alpha1) CredentialIssuerConfigs(namespace string) v1alpha1. return &FakeCredentialIssuerConfigs{c, namespace} } +func (c *FakeConfigV1alpha1) OIDCProviderConfigs(namespace string) v1alpha1.OIDCProviderConfigInterface { + return &FakeOIDCProviderConfigs{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeConfigV1alpha1) RESTClient() rest.Interface { diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go new file mode 100644 index 000000000..fc4305808 --- /dev/null +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -0,0 +1,117 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "go.pinniped.dev/generated/1.18/apis/config/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeOIDCProviderConfigs implements OIDCProviderConfigInterface +type FakeOIDCProviderConfigs struct { + Fake *FakeConfigV1alpha1 + ns string +} + +var oidcproviderconfigsResource = schema.GroupVersionResource{Group: "config.pinniped.dev", Version: "v1alpha1", Resource: "oidcproviderconfigs"} + +var oidcproviderconfigsKind = schema.GroupVersionKind{Group: "config.pinniped.dev", Version: "v1alpha1", Kind: "OIDCProviderConfig"} + +// Get takes name of the oIDCProviderConfig, and returns the corresponding oIDCProviderConfig object, and an error if there is any. +func (c *FakeOIDCProviderConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(oidcproviderconfigsResource, c.ns, name), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// List takes label and field selectors, and returns the list of OIDCProviderConfigs that match those selectors. +func (c *FakeOIDCProviderConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.OIDCProviderConfigList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(oidcproviderconfigsResource, oidcproviderconfigsKind, c.ns, opts), &v1alpha1.OIDCProviderConfigList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.OIDCProviderConfigList{ListMeta: obj.(*v1alpha1.OIDCProviderConfigList).ListMeta} + for _, item := range obj.(*v1alpha1.OIDCProviderConfigList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested oIDCProviderConfigs. +func (c *FakeOIDCProviderConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(oidcproviderconfigsResource, c.ns, opts)) + +} + +// Create takes the representation of a oIDCProviderConfig and creates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *FakeOIDCProviderConfigs) Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(oidcproviderconfigsResource, c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// Update takes the representation of a oIDCProviderConfig and updates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *FakeOIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(oidcproviderconfigsResource, c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. +func (c *FakeOIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(oidcproviderconfigsResource, c.ns, name), &v1alpha1.OIDCProviderConfig{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeOIDCProviderConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(oidcproviderconfigsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.OIDCProviderConfigList{}) + return err +} + +// Patch applies the patch and returns the patched oIDCProviderConfig. +func (c *FakeOIDCProviderConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(oidcproviderconfigsResource, c.ns, name, pt, data, subresources...), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go index 61507202b..5da5f449c 100644 --- a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go @@ -6,3 +6,5 @@ package v1alpha1 type CredentialIssuerConfigExpansion interface{} + +type OIDCProviderConfigExpansion interface{} diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..99042aa34 --- /dev/null +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,165 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "go.pinniped.dev/generated/1.18/apis/config/v1alpha1" + scheme "go.pinniped.dev/generated/1.18/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// OIDCProviderConfigsGetter has a method to return a OIDCProviderConfigInterface. +// A group's client should implement this interface. +type OIDCProviderConfigsGetter interface { + OIDCProviderConfigs(namespace string) OIDCProviderConfigInterface +} + +// OIDCProviderConfigInterface has methods to work with OIDCProviderConfig resources. +type OIDCProviderConfigInterface interface { + Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (*v1alpha1.OIDCProviderConfig, error) + Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.OIDCProviderConfig, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.OIDCProviderConfigList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) + OIDCProviderConfigExpansion +} + +// oIDCProviderConfigs implements OIDCProviderConfigInterface +type oIDCProviderConfigs struct { + client rest.Interface + ns string +} + +// newOIDCProviderConfigs returns a OIDCProviderConfigs +func newOIDCProviderConfigs(c *ConfigV1alpha1Client, namespace string) *oIDCProviderConfigs { + return &oIDCProviderConfigs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the oIDCProviderConfig, and returns the corresponding oIDCProviderConfig object, and an error if there is any. +func (c *oIDCProviderConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of OIDCProviderConfigs that match those selectors. +func (c *oIDCProviderConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.OIDCProviderConfigList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.OIDCProviderConfigList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested oIDCProviderConfigs. +func (c *oIDCProviderConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a oIDCProviderConfig and creates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *oIDCProviderConfigs) Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Post(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(oIDCProviderConfig). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a oIDCProviderConfig and updates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *oIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(oIDCProviderConfig.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(oIDCProviderConfig). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. +func (c *oIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *oIDCProviderConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched oIDCProviderConfig. +func (c *oIDCProviderConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/generated/1.18/client/informers/externalversions/config/v1alpha1/interface.go b/generated/1.18/client/informers/externalversions/config/v1alpha1/interface.go index 71b872b10..479012452 100644 --- a/generated/1.18/client/informers/externalversions/config/v1alpha1/interface.go +++ b/generated/1.18/client/informers/externalversions/config/v1alpha1/interface.go @@ -13,6 +13,8 @@ import ( type Interface interface { // CredentialIssuerConfigs returns a CredentialIssuerConfigInformer. CredentialIssuerConfigs() CredentialIssuerConfigInformer + // OIDCProviderConfigs returns a OIDCProviderConfigInformer. + OIDCProviderConfigs() OIDCProviderConfigInformer } type version struct { @@ -30,3 +32,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (v *version) CredentialIssuerConfigs() CredentialIssuerConfigInformer { return &credentialIssuerConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// OIDCProviderConfigs returns a OIDCProviderConfigInformer. +func (v *version) OIDCProviderConfigs() OIDCProviderConfigInformer { + return &oIDCProviderConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/generated/1.18/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go b/generated/1.18/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..8861fcfa1 --- /dev/null +++ b/generated/1.18/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,77 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + configv1alpha1 "go.pinniped.dev/generated/1.18/apis/config/v1alpha1" + versioned "go.pinniped.dev/generated/1.18/client/clientset/versioned" + internalinterfaces "go.pinniped.dev/generated/1.18/client/informers/externalversions/internalinterfaces" + v1alpha1 "go.pinniped.dev/generated/1.18/client/listers/config/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// OIDCProviderConfigInformer provides access to a shared informer and lister for +// OIDCProviderConfigs. +type OIDCProviderConfigInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.OIDCProviderConfigLister +} + +type oIDCProviderConfigInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewOIDCProviderConfigInformer constructs a new informer for OIDCProviderConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewOIDCProviderConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredOIDCProviderConfigInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredOIDCProviderConfigInformer constructs a new informer for OIDCProviderConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredOIDCProviderConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ConfigV1alpha1().OIDCProviderConfigs(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ConfigV1alpha1().OIDCProviderConfigs(namespace).Watch(context.TODO(), options) + }, + }, + &configv1alpha1.OIDCProviderConfig{}, + resyncPeriod, + indexers, + ) +} + +func (f *oIDCProviderConfigInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredOIDCProviderConfigInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *oIDCProviderConfigInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&configv1alpha1.OIDCProviderConfig{}, f.defaultInformer) +} + +func (f *oIDCProviderConfigInformer) Lister() v1alpha1.OIDCProviderConfigLister { + return v1alpha1.NewOIDCProviderConfigLister(f.Informer().GetIndexer()) +} diff --git a/generated/1.18/client/informers/externalversions/generic.go b/generated/1.18/client/informers/externalversions/generic.go index bba1a47a3..fa7671c92 100644 --- a/generated/1.18/client/informers/externalversions/generic.go +++ b/generated/1.18/client/informers/externalversions/generic.go @@ -44,6 +44,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=config.pinniped.dev, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("credentialissuerconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().CredentialIssuerConfigs().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("oidcproviderconfigs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().OIDCProviderConfigs().Informer()}, nil // Group=idp.pinniped.dev, Version=v1alpha1 case idpv1alpha1.SchemeGroupVersion.WithResource("webhookidentityproviders"): diff --git a/generated/1.18/client/listers/config/v1alpha1/expansion_generated.go b/generated/1.18/client/listers/config/v1alpha1/expansion_generated.go index 68c725a96..7f0b5b9d5 100644 --- a/generated/1.18/client/listers/config/v1alpha1/expansion_generated.go +++ b/generated/1.18/client/listers/config/v1alpha1/expansion_generated.go @@ -12,3 +12,11 @@ type CredentialIssuerConfigListerExpansion interface{} // CredentialIssuerConfigNamespaceListerExpansion allows custom methods to be added to // CredentialIssuerConfigNamespaceLister. type CredentialIssuerConfigNamespaceListerExpansion interface{} + +// OIDCProviderConfigListerExpansion allows custom methods to be added to +// OIDCProviderConfigLister. +type OIDCProviderConfigListerExpansion interface{} + +// OIDCProviderConfigNamespaceListerExpansion allows custom methods to be added to +// OIDCProviderConfigNamespaceLister. +type OIDCProviderConfigNamespaceListerExpansion interface{} diff --git a/generated/1.18/client/listers/config/v1alpha1/oidcproviderconfig.go b/generated/1.18/client/listers/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..d567208cc --- /dev/null +++ b/generated/1.18/client/listers/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,81 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "go.pinniped.dev/generated/1.18/apis/config/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// OIDCProviderConfigLister helps list OIDCProviderConfigs. +type OIDCProviderConfigLister interface { + // List lists all OIDCProviderConfigs in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) + // OIDCProviderConfigs returns an object that can list and get OIDCProviderConfigs. + OIDCProviderConfigs(namespace string) OIDCProviderConfigNamespaceLister + OIDCProviderConfigListerExpansion +} + +// oIDCProviderConfigLister implements the OIDCProviderConfigLister interface. +type oIDCProviderConfigLister struct { + indexer cache.Indexer +} + +// NewOIDCProviderConfigLister returns a new OIDCProviderConfigLister. +func NewOIDCProviderConfigLister(indexer cache.Indexer) OIDCProviderConfigLister { + return &oIDCProviderConfigLister{indexer: indexer} +} + +// List lists all OIDCProviderConfigs in the indexer. +func (s *oIDCProviderConfigLister) List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.OIDCProviderConfig)) + }) + return ret, err +} + +// OIDCProviderConfigs returns an object that can list and get OIDCProviderConfigs. +func (s *oIDCProviderConfigLister) OIDCProviderConfigs(namespace string) OIDCProviderConfigNamespaceLister { + return oIDCProviderConfigNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// OIDCProviderConfigNamespaceLister helps list and get OIDCProviderConfigs. +type OIDCProviderConfigNamespaceLister interface { + // List lists all OIDCProviderConfigs in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) + // Get retrieves the OIDCProviderConfig from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.OIDCProviderConfig, error) + OIDCProviderConfigNamespaceListerExpansion +} + +// oIDCProviderConfigNamespaceLister implements the OIDCProviderConfigNamespaceLister +// interface. +type oIDCProviderConfigNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all OIDCProviderConfigs in the indexer for a given namespace. +func (s oIDCProviderConfigNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.OIDCProviderConfig)) + }) + return ret, err +} + +// Get retrieves the OIDCProviderConfig from the indexer for a given namespace and name. +func (s oIDCProviderConfigNamespaceLister) Get(name string) (*v1alpha1.OIDCProviderConfig, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("oidcproviderconfig"), name) + } + return obj.(*v1alpha1.OIDCProviderConfig), nil +} diff --git a/generated/1.18/client/openapi/zz_generated.openapi.go b/generated/1.18/client/openapi/zz_generated.openapi.go index ac54aea2a..76c671b6e 100644 --- a/generated/1.18/client/openapi/zz_generated.openapi.go +++ b/generated/1.18/client/openapi/zz_generated.openapi.go @@ -22,6 +22,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.CredentialIssuerConfigList": schema_118_apis_config_v1alpha1_CredentialIssuerConfigList(ref), "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.CredentialIssuerConfigStatus": schema_118_apis_config_v1alpha1_CredentialIssuerConfigStatus(ref), "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.CredentialIssuerConfigStrategy": schema_118_apis_config_v1alpha1_CredentialIssuerConfigStrategy(ref), + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfig": schema_118_apis_config_v1alpha1_OIDCProviderConfig(ref), + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigList": schema_118_apis_config_v1alpha1_OIDCProviderConfigList(ref), + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec": schema_118_apis_config_v1alpha1_OIDCProviderConfigSpec(ref), "go.pinniped.dev/generated/1.18/apis/idp/v1alpha1.Condition": schema_118_apis_idp_v1alpha1_Condition(ref), "go.pinniped.dev/generated/1.18/apis/idp/v1alpha1.TLSSpec": schema_118_apis_idp_v1alpha1_TLSSpec(ref), "go.pinniped.dev/generated/1.18/apis/idp/v1alpha1.WebhookIdentityProvider": schema_118_apis_idp_v1alpha1_WebhookIdentityProvider(ref), @@ -286,6 +289,114 @@ func schema_118_apis_config_v1alpha1_CredentialIssuerConfigStrategy(ref common.R } } +func schema_118_apis_config_v1alpha1_OIDCProviderConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfig describes the configuration of an OIDC provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Spec of the OIDC provider.", + Ref: ref("go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec"), + }, + }, + }, + Required: []string{"status"}, + }, + }, + Dependencies: []string{ + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_118_apis_config_v1alpha1_OIDCProviderConfigList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfig"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfig", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_118_apis_config_v1alpha1_OIDCProviderConfigSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfigSpec is a struct that describes an OIDC Provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "issuer": { + SchemaProps: spec.SchemaProps{ + Description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the identifier that it will use for the iss claim in issued JWTs. This field will also be used as the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is https://example.com/foo, then your authorization endpoint will look like https://example.com/foo/some/path/to/auth/endpoint).\n\nSee https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"issuer"}, + }, + }, + } +} + func schema_118_apis_idp_v1alpha1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml new file mode 100644 index 000000000..961da2538 --- /dev/null +++ b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,65 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.0 + creationTimestamp: null + name: oidcproviderconfigs.config.pinniped.dev +spec: + group: config.pinniped.dev + names: + kind: OIDCProviderConfig + listKind: OIDCProviderConfigList + plural: oidcproviderconfigs + shortNames: + - opc + singular: oidcproviderconfig + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: OIDCProviderConfig describes the configuration of an OIDC provider. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + status: + description: Spec of the OIDC provider. + properties: + issuer: + description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery + Metadata document, as well as the identifier that it will use for + the iss claim in issued JWTs. This field will also be used as the + base URL for any endpoints used by the OIDC Provider (e.g., if your + issuer is https://example.com/foo, then your authorization endpoint + will look like https://example.com/foo/some/path/to/auth/endpoint). + \n See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 + for more information." + minLength: 1 + type: string + required: + - issuer + type: object + required: + - status + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/generated/1.19/README.adoc b/generated/1.19/README.adoc index 9e0a6579d..6de470167 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -95,6 +95,45 @@ Status of a credential issuer. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfig"] +==== OIDCProviderConfig + +OIDCProviderConfig describes the configuration of an OIDC provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfiglist[$$OIDCProviderConfigList$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. + +| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +|=== + + + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfigspec"] +==== OIDCProviderConfigSpec + +OIDCProviderConfigSpec is a struct that describes an OIDC Provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfig[$$OIDCProviderConfig$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`issuer`* __string__ | Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the identifier that it will use for the iss claim in issued JWTs. This field will also be used as the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is https://example.com/foo, then your authorization endpoint will look like https://example.com/foo/some/path/to/auth/endpoint). + See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 diff --git a/generated/1.19/apis/config/v1alpha1/types.go b/generated/1.19/apis/config/v1alpha1/types_credentialissuerconfig.go similarity index 100% rename from generated/1.19/apis/config/v1alpha1/types.go rename to generated/1.19/apis/config/v1alpha1/types_credentialissuerconfig.go diff --git a/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go new file mode 100644 index 000000000..7000aead5 --- /dev/null +++ b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -0,0 +1,42 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// OIDCProviderConfigSpec is a struct that describes an OIDC Provider. +type OIDCProviderConfigSpec struct { + // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the + // identifier that it will use for the iss claim in issued JWTs. This field will also be used as + // the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is + // https://example.com/foo, then your authorization endpoint will look like + // https://example.com/foo/some/path/to/auth/endpoint). + // + // See + // https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information. + // +kubebuilder:validation:MinLength=1 + Issuer string `json:"issuer"` +} + +// OIDCProviderConfig describes the configuration of an OIDC provider. +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=opc +type OIDCProviderConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec of the OIDC provider. + Spec OIDCProviderConfigSpec `json:"status"` +} + +// List of OIDCProviderConfig objects. +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type OIDCProviderConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []OIDCProviderConfig `json:"items"` +} diff --git a/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go index 4e72d35da..f45d80c35 100644 --- a/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -131,3 +131,79 @@ func (in *CredentialIssuerConfigStrategy) DeepCopy() *CredentialIssuerConfigStra in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfig. +func (in *OIDCProviderConfig) DeepCopy() *OIDCProviderConfig { + if in == nil { + return nil + } + out := new(OIDCProviderConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OIDCProviderConfig) 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 *OIDCProviderConfigList) DeepCopyInto(out *OIDCProviderConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OIDCProviderConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigList. +func (in *OIDCProviderConfigList) DeepCopy() *OIDCProviderConfigList { + if in == nil { + return nil + } + out := new(OIDCProviderConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OIDCProviderConfigList) 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 *OIDCProviderConfigSpec) DeepCopyInto(out *OIDCProviderConfigSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigSpec. +func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { + if in == nil { + return nil + } + out := new(OIDCProviderConfigSpec) + in.DeepCopyInto(out) + return out +} diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/config_client.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/config_client.go index 04fa22e59..0701323f5 100644 --- a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/config_client.go +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/config_client.go @@ -14,6 +14,7 @@ import ( type ConfigV1alpha1Interface interface { RESTClient() rest.Interface CredentialIssuerConfigsGetter + OIDCProviderConfigsGetter } // ConfigV1alpha1Client is used to interact with features provided by the config.pinniped.dev group. @@ -25,6 +26,10 @@ func (c *ConfigV1alpha1Client) CredentialIssuerConfigs(namespace string) Credent return newCredentialIssuerConfigs(c, namespace) } +func (c *ConfigV1alpha1Client) OIDCProviderConfigs(namespace string) OIDCProviderConfigInterface { + return newOIDCProviderConfigs(c, namespace) +} + // NewForConfig creates a new ConfigV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*ConfigV1alpha1Client, error) { config := *c diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go index 5c1235c55..8edfd1360 100644 --- a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_config_client.go @@ -19,6 +19,10 @@ func (c *FakeConfigV1alpha1) CredentialIssuerConfigs(namespace string) v1alpha1. return &FakeCredentialIssuerConfigs{c, namespace} } +func (c *FakeConfigV1alpha1) OIDCProviderConfigs(namespace string) v1alpha1.OIDCProviderConfigInterface { + return &FakeOIDCProviderConfigs{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeConfigV1alpha1) RESTClient() rest.Interface { diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go new file mode 100644 index 000000000..8e01a0672 --- /dev/null +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -0,0 +1,117 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeOIDCProviderConfigs implements OIDCProviderConfigInterface +type FakeOIDCProviderConfigs struct { + Fake *FakeConfigV1alpha1 + ns string +} + +var oidcproviderconfigsResource = schema.GroupVersionResource{Group: "config.pinniped.dev", Version: "v1alpha1", Resource: "oidcproviderconfigs"} + +var oidcproviderconfigsKind = schema.GroupVersionKind{Group: "config.pinniped.dev", Version: "v1alpha1", Kind: "OIDCProviderConfig"} + +// Get takes name of the oIDCProviderConfig, and returns the corresponding oIDCProviderConfig object, and an error if there is any. +func (c *FakeOIDCProviderConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(oidcproviderconfigsResource, c.ns, name), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// List takes label and field selectors, and returns the list of OIDCProviderConfigs that match those selectors. +func (c *FakeOIDCProviderConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.OIDCProviderConfigList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(oidcproviderconfigsResource, oidcproviderconfigsKind, c.ns, opts), &v1alpha1.OIDCProviderConfigList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.OIDCProviderConfigList{ListMeta: obj.(*v1alpha1.OIDCProviderConfigList).ListMeta} + for _, item := range obj.(*v1alpha1.OIDCProviderConfigList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested oIDCProviderConfigs. +func (c *FakeOIDCProviderConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(oidcproviderconfigsResource, c.ns, opts)) + +} + +// Create takes the representation of a oIDCProviderConfig and creates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *FakeOIDCProviderConfigs) Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(oidcproviderconfigsResource, c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// Update takes the representation of a oIDCProviderConfig and updates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *FakeOIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(oidcproviderconfigsResource, c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + +// Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. +func (c *FakeOIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(oidcproviderconfigsResource, c.ns, name), &v1alpha1.OIDCProviderConfig{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeOIDCProviderConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(oidcproviderconfigsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.OIDCProviderConfigList{}) + return err +} + +// Patch applies the patch and returns the patched oIDCProviderConfig. +func (c *FakeOIDCProviderConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(oidcproviderconfigsResource, c.ns, name, pt, data, subresources...), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go index 61507202b..5da5f449c 100644 --- a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/generated_expansion.go @@ -6,3 +6,5 @@ package v1alpha1 type CredentialIssuerConfigExpansion interface{} + +type OIDCProviderConfigExpansion interface{} diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..7b32bf1e6 --- /dev/null +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,165 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + scheme "go.pinniped.dev/generated/1.19/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// OIDCProviderConfigsGetter has a method to return a OIDCProviderConfigInterface. +// A group's client should implement this interface. +type OIDCProviderConfigsGetter interface { + OIDCProviderConfigs(namespace string) OIDCProviderConfigInterface +} + +// OIDCProviderConfigInterface has methods to work with OIDCProviderConfig resources. +type OIDCProviderConfigInterface interface { + Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (*v1alpha1.OIDCProviderConfig, error) + Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.OIDCProviderConfig, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.OIDCProviderConfigList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) + OIDCProviderConfigExpansion +} + +// oIDCProviderConfigs implements OIDCProviderConfigInterface +type oIDCProviderConfigs struct { + client rest.Interface + ns string +} + +// newOIDCProviderConfigs returns a OIDCProviderConfigs +func newOIDCProviderConfigs(c *ConfigV1alpha1Client, namespace string) *oIDCProviderConfigs { + return &oIDCProviderConfigs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the oIDCProviderConfig, and returns the corresponding oIDCProviderConfig object, and an error if there is any. +func (c *oIDCProviderConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of OIDCProviderConfigs that match those selectors. +func (c *oIDCProviderConfigs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.OIDCProviderConfigList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.OIDCProviderConfigList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested oIDCProviderConfigs. +func (c *oIDCProviderConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a oIDCProviderConfig and creates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *oIDCProviderConfigs) Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Post(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(oIDCProviderConfig). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a oIDCProviderConfig and updates it. Returns the server's representation of the oIDCProviderConfig, and an error, if there is any. +func (c *oIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(oIDCProviderConfig.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(oIDCProviderConfig). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. +func (c *oIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *oIDCProviderConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched oIDCProviderConfig. +func (c *oIDCProviderConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/generated/1.19/client/informers/externalversions/config/v1alpha1/interface.go b/generated/1.19/client/informers/externalversions/config/v1alpha1/interface.go index 65e3d1095..16d512351 100644 --- a/generated/1.19/client/informers/externalversions/config/v1alpha1/interface.go +++ b/generated/1.19/client/informers/externalversions/config/v1alpha1/interface.go @@ -13,6 +13,8 @@ import ( type Interface interface { // CredentialIssuerConfigs returns a CredentialIssuerConfigInformer. CredentialIssuerConfigs() CredentialIssuerConfigInformer + // OIDCProviderConfigs returns a OIDCProviderConfigInformer. + OIDCProviderConfigs() OIDCProviderConfigInformer } type version struct { @@ -30,3 +32,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (v *version) CredentialIssuerConfigs() CredentialIssuerConfigInformer { return &credentialIssuerConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// OIDCProviderConfigs returns a OIDCProviderConfigInformer. +func (v *version) OIDCProviderConfigs() OIDCProviderConfigInformer { + return &oIDCProviderConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/generated/1.19/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go b/generated/1.19/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..1dfdfd821 --- /dev/null +++ b/generated/1.19/client/informers/externalversions/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,77 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + configv1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + versioned "go.pinniped.dev/generated/1.19/client/clientset/versioned" + internalinterfaces "go.pinniped.dev/generated/1.19/client/informers/externalversions/internalinterfaces" + v1alpha1 "go.pinniped.dev/generated/1.19/client/listers/config/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// OIDCProviderConfigInformer provides access to a shared informer and lister for +// OIDCProviderConfigs. +type OIDCProviderConfigInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.OIDCProviderConfigLister +} + +type oIDCProviderConfigInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewOIDCProviderConfigInformer constructs a new informer for OIDCProviderConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewOIDCProviderConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredOIDCProviderConfigInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredOIDCProviderConfigInformer constructs a new informer for OIDCProviderConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredOIDCProviderConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ConfigV1alpha1().OIDCProviderConfigs(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ConfigV1alpha1().OIDCProviderConfigs(namespace).Watch(context.TODO(), options) + }, + }, + &configv1alpha1.OIDCProviderConfig{}, + resyncPeriod, + indexers, + ) +} + +func (f *oIDCProviderConfigInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredOIDCProviderConfigInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *oIDCProviderConfigInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&configv1alpha1.OIDCProviderConfig{}, f.defaultInformer) +} + +func (f *oIDCProviderConfigInformer) Lister() v1alpha1.OIDCProviderConfigLister { + return v1alpha1.NewOIDCProviderConfigLister(f.Informer().GetIndexer()) +} diff --git a/generated/1.19/client/informers/externalversions/generic.go b/generated/1.19/client/informers/externalversions/generic.go index 6a761df3f..b80a9fece 100644 --- a/generated/1.19/client/informers/externalversions/generic.go +++ b/generated/1.19/client/informers/externalversions/generic.go @@ -44,6 +44,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=config.pinniped.dev, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("credentialissuerconfigs"): return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().CredentialIssuerConfigs().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("oidcproviderconfigs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().OIDCProviderConfigs().Informer()}, nil // Group=idp.pinniped.dev, Version=v1alpha1 case idpv1alpha1.SchemeGroupVersion.WithResource("webhookidentityproviders"): diff --git a/generated/1.19/client/listers/config/v1alpha1/expansion_generated.go b/generated/1.19/client/listers/config/v1alpha1/expansion_generated.go index 68c725a96..7f0b5b9d5 100644 --- a/generated/1.19/client/listers/config/v1alpha1/expansion_generated.go +++ b/generated/1.19/client/listers/config/v1alpha1/expansion_generated.go @@ -12,3 +12,11 @@ type CredentialIssuerConfigListerExpansion interface{} // CredentialIssuerConfigNamespaceListerExpansion allows custom methods to be added to // CredentialIssuerConfigNamespaceLister. type CredentialIssuerConfigNamespaceListerExpansion interface{} + +// OIDCProviderConfigListerExpansion allows custom methods to be added to +// OIDCProviderConfigLister. +type OIDCProviderConfigListerExpansion interface{} + +// OIDCProviderConfigNamespaceListerExpansion allows custom methods to be added to +// OIDCProviderConfigNamespaceLister. +type OIDCProviderConfigNamespaceListerExpansion interface{} diff --git a/generated/1.19/client/listers/config/v1alpha1/oidcproviderconfig.go b/generated/1.19/client/listers/config/v1alpha1/oidcproviderconfig.go new file mode 100644 index 000000000..7d07e3309 --- /dev/null +++ b/generated/1.19/client/listers/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,86 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// OIDCProviderConfigLister helps list OIDCProviderConfigs. +// All objects returned here must be treated as read-only. +type OIDCProviderConfigLister interface { + // List lists all OIDCProviderConfigs in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) + // OIDCProviderConfigs returns an object that can list and get OIDCProviderConfigs. + OIDCProviderConfigs(namespace string) OIDCProviderConfigNamespaceLister + OIDCProviderConfigListerExpansion +} + +// oIDCProviderConfigLister implements the OIDCProviderConfigLister interface. +type oIDCProviderConfigLister struct { + indexer cache.Indexer +} + +// NewOIDCProviderConfigLister returns a new OIDCProviderConfigLister. +func NewOIDCProviderConfigLister(indexer cache.Indexer) OIDCProviderConfigLister { + return &oIDCProviderConfigLister{indexer: indexer} +} + +// List lists all OIDCProviderConfigs in the indexer. +func (s *oIDCProviderConfigLister) List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.OIDCProviderConfig)) + }) + return ret, err +} + +// OIDCProviderConfigs returns an object that can list and get OIDCProviderConfigs. +func (s *oIDCProviderConfigLister) OIDCProviderConfigs(namespace string) OIDCProviderConfigNamespaceLister { + return oIDCProviderConfigNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// OIDCProviderConfigNamespaceLister helps list and get OIDCProviderConfigs. +// All objects returned here must be treated as read-only. +type OIDCProviderConfigNamespaceLister interface { + // List lists all OIDCProviderConfigs in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) + // Get retrieves the OIDCProviderConfig from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.OIDCProviderConfig, error) + OIDCProviderConfigNamespaceListerExpansion +} + +// oIDCProviderConfigNamespaceLister implements the OIDCProviderConfigNamespaceLister +// interface. +type oIDCProviderConfigNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all OIDCProviderConfigs in the indexer for a given namespace. +func (s oIDCProviderConfigNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.OIDCProviderConfig, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.OIDCProviderConfig)) + }) + return ret, err +} + +// Get retrieves the OIDCProviderConfig from the indexer for a given namespace and name. +func (s oIDCProviderConfigNamespaceLister) Get(name string) (*v1alpha1.OIDCProviderConfig, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("oidcproviderconfig"), name) + } + return obj.(*v1alpha1.OIDCProviderConfig), nil +} diff --git a/generated/1.19/client/openapi/zz_generated.openapi.go b/generated/1.19/client/openapi/zz_generated.openapi.go index c27ff8c0a..95454e7be 100644 --- a/generated/1.19/client/openapi/zz_generated.openapi.go +++ b/generated/1.19/client/openapi/zz_generated.openapi.go @@ -22,6 +22,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.CredentialIssuerConfigList": schema_119_apis_config_v1alpha1_CredentialIssuerConfigList(ref), "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.CredentialIssuerConfigStatus": schema_119_apis_config_v1alpha1_CredentialIssuerConfigStatus(ref), "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.CredentialIssuerConfigStrategy": schema_119_apis_config_v1alpha1_CredentialIssuerConfigStrategy(ref), + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfig": schema_119_apis_config_v1alpha1_OIDCProviderConfig(ref), + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigList": schema_119_apis_config_v1alpha1_OIDCProviderConfigList(ref), + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec": schema_119_apis_config_v1alpha1_OIDCProviderConfigSpec(ref), "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1.Condition": schema_119_apis_idp_v1alpha1_Condition(ref), "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1.TLSSpec": schema_119_apis_idp_v1alpha1_TLSSpec(ref), "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1.WebhookIdentityProvider": schema_119_apis_idp_v1alpha1_WebhookIdentityProvider(ref), @@ -287,6 +290,114 @@ func schema_119_apis_config_v1alpha1_CredentialIssuerConfigStrategy(ref common.R } } +func schema_119_apis_config_v1alpha1_OIDCProviderConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfig describes the configuration of an OIDC provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Spec of the OIDC provider.", + Ref: ref("go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec"), + }, + }, + }, + Required: []string{"status"}, + }, + }, + Dependencies: []string{ + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_119_apis_config_v1alpha1_OIDCProviderConfigList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfig"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfig", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_119_apis_config_v1alpha1_OIDCProviderConfigSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfigSpec is a struct that describes an OIDC Provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "issuer": { + SchemaProps: spec.SchemaProps{ + Description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the identifier that it will use for the iss claim in issued JWTs. This field will also be used as the base URL for any endpoints used by the OIDC Provider (e.g., if your issuer is https://example.com/foo, then your authorization endpoint will look like https://example.com/foo/some/path/to/auth/endpoint).\n\nSee https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"issuer"}, + }, + }, + } +} + func schema_119_apis_idp_v1alpha1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml new file mode 100644 index 000000000..961da2538 --- /dev/null +++ b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,65 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.0 + creationTimestamp: null + name: oidcproviderconfigs.config.pinniped.dev +spec: + group: config.pinniped.dev + names: + kind: OIDCProviderConfig + listKind: OIDCProviderConfigList + plural: oidcproviderconfigs + shortNames: + - opc + singular: oidcproviderconfig + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: OIDCProviderConfig describes the configuration of an OIDC provider. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + status: + description: Spec of the OIDC provider. + properties: + issuer: + description: "Issuer is the OIDC Provider's issuer, per the OIDC Discovery + Metadata document, as well as the identifier that it will use for + the iss claim in issued JWTs. This field will also be used as the + base URL for any endpoints used by the OIDC Provider (e.g., if your + issuer is https://example.com/foo, then your authorization endpoint + will look like https://example.com/foo/some/path/to/auth/endpoint). + \n See https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 + for more information." + minLength: 1 + type: string + required: + - issuer + type: object + required: + - status + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go new file mode 100644 index 000000000..80b6c5199 --- /dev/null +++ b/test/integration/supervisor_discovery_test.go @@ -0,0 +1,37 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.pinniped.dev/test/library" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestSupervisorOIDCDiscovery(t *testing.T) { + env := library.IntegrationEnv(t) + client := library.NewPinnipedClientset(t) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + _, err := client. + ConfigV1alpha1(). + OIDCProviderConfigs(env.Namespace). + List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + + // 0. Create CRD with single issuer field in config group and generate code. + // 1. Add test hook that restores these CRDs at the end of the test. + // 2. Get all CRDs and save them in an array somewhere; also delete them after we store them. + // 3. Test behavior of when we have no CRD - make sure we get the status code that we want back + // from the discovery endpoint? + // 4. Add a CRD with a known issuer. + // 5. Test behavior of when we have a CRD - make sure we get the status code and response body + // that we want back from the discovery endpoint? +} From 78cc49d658f17d5987155938ac2e77681c2e2ad6 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 6 Oct 2020 13:35:05 -0700 Subject: [PATCH 06/28] Revert "supervisor-oidc: create dynamic config in YTT templates" This reverts commit 006d96ab927362361779e940f814858d324453be. --- deploy-supervisor/deployment.yaml | 23 ----------------------- deploy-supervisor/values.yaml | 6 ------ hack/prepare-for-integration-tests.sh | 6 +----- 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/deploy-supervisor/deployment.yaml b/deploy-supervisor/deployment.yaml index bb2f09564..cd4079c8a 100644 --- a/deploy-supervisor/deployment.yaml +++ b/deploy-supervisor/deployment.yaml @@ -30,29 +30,6 @@ data: names: dynamicConfigMap: (@= data.values.app_name + "-dynamic-config" @) --- -apiVersion: v1 -kind: ConfigMap -metadata: - name: #@ data.values.app_name + "-dynamic-config" - namespace: #@ data.values.namespace - labels: - app: #@ data.values.app_name -data: - issuer: #@ data.values.issuer_url ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: #@ data.values.app_name + "-static-config" - namespace: #@ data.values.namespace - labels: - app: #@ data.values.app_name -data: - #@yaml/text-templated-strings - pinniped.yaml: | - names: - dynamicConfigMap: (@= data.values.app_name + "-dynamic-config" @) ---- #@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "": apiVersion: v1 kind: Secret diff --git a/deploy-supervisor/values.yaml b/deploy-supervisor/values.yaml index ec0430a95..6df6efe99 100644 --- a/deploy-supervisor/values.yaml +++ b/deploy-supervisor/values.yaml @@ -20,9 +20,3 @@ image_tag: latest #! Typically the value would be the output of: kubectl create secret docker-registry x --docker-server=https://example.io --docker-username="USERNAME" --docker-password="PASSWORD" --dry-run=client -o json | jq -r '.data[".dockerconfigjson"]' #! Optional. image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}} - -#! Specifies the base URL used in the endpoint fields (e.g., authorization_endpoint, jwks_url, etc.) -#! of the OpenID Provider Metadata, as well as the value of the iss JWT claim that will be used by -#! this OIDC provider. Per the OIDC Discovery spec, this URL must use the HTTPS scheme. See -#! https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3. -issuer_url: #! e.g., https://auth.my-org.com diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index f5d5073f9..05f942881 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -177,16 +177,12 @@ kubectl create secret generic "$test_username" \ # # Deploy the Pinniped Supervisor # -issuer_url=https://todo.what-should-this-be - pushd deploy-supervisor >/dev/null log_note "Deploying the Pinniped Supervisor app to the cluster..." ytt --file . \ --data-value "image_repo=$registry_repo" \ - --data-value "image_tag=$tag" \ - --data-value "issuer_url=$issuer_url" \ - >"$manifest" + --data-value "image_tag=$tag" >"$manifest" kapp deploy --yes --app "pinniped-supervisor" --diff-changes --file "$manifest" From ae56fcb46a3ddc2af32e16b74d9477273b30f930 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Tue, 6 Oct 2020 17:53:29 -0700 Subject: [PATCH 07/28] Add integration test for the OIDC discovery endpoint - Intended to be a red test in this commit; will make it go green in a future commit - Enhance env.go and prepare-for-integration-tests.sh to make it possible to write integration tests for the supervisor app by setting more env vars and by exposing the service to the kind host on a localhost port - Add `--clean` option to prepare-for-integration-tests.sh to make it easier to start fresh - Make prepare-for-integration-tests.sh advise you to run `go test -v -count 1 ./test/integration` because this does not buffer the test output - Make concierge_api_discovery_test.go pass by adding expectations for the new OIDCProviderConfig type --- hack/lib/kind-config/multi-node.yaml | 8 ++ hack/lib/kind-config/single-node.yaml | 5 + hack/prepare-for-integration-tests.sh | 42 ++++++- .../concierge_api_discovery_test.go | 8 ++ test/integration/supervisor_discovery_test.go | 113 +++++++++++++++--- test/library/env.go | 16 ++- 6 files changed, 170 insertions(+), 22 deletions(-) create mode 100644 hack/lib/kind-config/multi-node.yaml create mode 100644 hack/lib/kind-config/single-node.yaml diff --git a/hack/lib/kind-config/multi-node.yaml b/hack/lib/kind-config/multi-node.yaml new file mode 100644 index 000000000..4e4105f69 --- /dev/null +++ b/hack/lib/kind-config/multi-node.yaml @@ -0,0 +1,8 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + - role: worker + extraPortMappings: [{containerPort: 31234, hostPort: 12345, protocol: TCP}] + - role: worker + extraPortMappings: [{containerPort: 31234, hostPort: 12345, protocol: TCP}] diff --git a/hack/lib/kind-config/single-node.yaml b/hack/lib/kind-config/single-node.yaml new file mode 100644 index 000000000..d62f3a594 --- /dev/null +++ b/hack/lib/kind-config/single-node.yaml @@ -0,0 +1,5 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + extraPortMappings: [{containerPort: 31234, hostPort: 12345, protocol: TCP}] diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index 05f942881..ba39ad4f6 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -42,6 +42,7 @@ function check_dependency() { # help=no skip_build=no +clean_kind=no while (("$#")); do case "$1" in @@ -53,6 +54,10 @@ while (("$#")); do skip_build=yes shift ;; + -c | --clean) + clean_kind=yes + shift + ;; -*) log_error "Unsupported flag $1" >&2 exit 1 @@ -90,17 +95,23 @@ check_dependency htpasswd "Please install htpasswd. Should be pre-installed on M # Require kubectl >= 1.18.x if [ "$(kubectl version --client=true --short | cut -d '.' -f 2)" -lt 18 ]; then - echo "kubectl >= 1.18.x is required, you have $(kubectl version --client=true --short | cut -d ':' -f2)" + log_error "kubectl >= 1.18.x is required, you have $(kubectl version --client=true --short | cut -d ':' -f2)" exit 1 fi +if [[ "$clean_kind" == "yes" ]]; then + log_note "Deleting running kind clusters to prepare from a clean slate..." + kind delete cluster +fi + # # Setup kind and build the app # log_note "Checking for running kind clusters..." if ! kind get clusters | grep -q -e '^kind$'; then log_note "Creating a kind cluster..." - kind create cluster + # single-node.yaml exposes node port 31234 as localhost:12345 + kind create cluster --config "$pinniped_path/hack/lib/kind-config/single-node.yaml" else if ! kubectl cluster-info | grep master | grep -q 127.0.0.1; then log_error "Seems like your kubeconfig is not targeting a local cluster." @@ -177,15 +188,37 @@ kubectl create secret generic "$test_username" \ # # Deploy the Pinniped Supervisor # +supervisor_app_name="pinniped-supervisor" +supervisor_namespace="pinniped-supervisor" + pushd deploy-supervisor >/dev/null log_note "Deploying the Pinniped Supervisor app to the cluster..." ytt --file . \ + --data-value "app_name=$supervisor_app_name" \ + --data-value "namespace=$supervisor_namespace" \ --data-value "image_repo=$registry_repo" \ --data-value "image_tag=$tag" >"$manifest" kapp deploy --yes --app "pinniped-supervisor" --diff-changes --file "$manifest" +log_note "Adding NodePort service to expose the Pinniped Supervisor app on the kind node..." +cat </dev/null # @@ -226,6 +259,9 @@ export PINNIPED_TEST_USER_GROUPS=${test_groups} export PINNIPED_TEST_USER_TOKEN=${test_username}:${test_password} export PINNIPED_TEST_WEBHOOK_ENDPOINT=${webhook_url} export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle} +export PINNIPED_SUPERVISOR_NAMESPACE=${namespace} +export PINNIPED_SUPERVISOR_APP_NAME=${app_name} +export PINNIPED_TEST_SUPERVISOR_ADDRESS="localhost:12345" read -r -d '' PINNIPED_CLUSTER_CAPABILITY_YAML << PINNIPED_CLUSTER_CAPABILITY_YAML_EOF || true ${pinniped_cluster_capability_file_content} @@ -242,7 +278,7 @@ goland_vars=$(grep -v '^#' /tmp/integration-test-env | grep -E '^export .+=' | s log_note log_note "🚀 Ready to run integration tests! For example..." log_note " cd $pinniped_path" -log_note ' source /tmp/integration-test-env && go test -v -count 1 ./test/...' +log_note ' source /tmp/integration-test-env && go test -v -count 1 ./test/integration' log_note log_note 'Want to run integration tests in GoLand? Copy/paste this "Environment" value for GoLand run configurations:' log_note " ${goland_vars}PINNIPED_CLUSTER_CAPABILITY_FILE=${kind_capabilities_file}" diff --git a/test/integration/concierge_api_discovery_test.go b/test/integration/concierge_api_discovery_test.go index d5eec0318..b1ff45a97 100644 --- a/test/integration/concierge_api_discovery_test.go +++ b/test/integration/concierge_api_discovery_test.go @@ -78,6 +78,14 @@ func TestGetAPIResourceList(t *testing.T) { Verbs: []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}, ShortNames: []string{"cic"}, }, + { + Name: "oidcproviderconfigs", + SingularName: "oidcproviderconfig", + Namespaced: true, + Kind: "OIDCProviderConfig", + Verbs: []string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}, + ShortNames: []string{"opc"}, + }, }, }, }, diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 80b6c5199..6c5cce780 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -5,33 +5,118 @@ package integration import ( "context" + "fmt" + "io/ioutil" + "net/http" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.pinniped.dev/test/library" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + "go.pinniped.dev/internal/here" + "go.pinniped.dev/test/library" ) func TestSupervisorOIDCDiscovery(t *testing.T) { env := library.IntegrationEnv(t) client := library.NewPinnipedClientset(t) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + httpClient := &http.Client{} + ns := env.SupervisorNamespace + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() - _, err := client. - ConfigV1alpha1(). - OIDCProviderConfigs(env.Namespace). - List(ctx, metav1.ListOptions{}) + // Temporarily remove any existing OIDCProviderConfigs from the cluster so we can test from a clean slate. + originalConfigList, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).List(ctx, metav1.ListOptions{}) require.NoError(t, err) - // 0. Create CRD with single issuer field in config group and generate code. - // 1. Add test hook that restores these CRDs at the end of the test. - // 2. Get all CRDs and save them in an array somewhere; also delete them after we store them. - // 3. Test behavior of when we have no CRD - make sure we get the status code that we want back - // from the discovery endpoint? - // 4. Add a CRD with a known issuer. - // 5. Test behavior of when we have a CRD - make sure we get the status code and response body - // that we want back from the discovery endpoint? + for _, config := range originalConfigList.Items { + err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(ctx, config.Name, metav1.DeleteOptions{}) + require.NoError(t, err) + } + + // When this test has finished, recreate any OIDCProviderConfigs that had existed on the cluster before this test. + t.Cleanup(func() { + for _, config := range originalConfigList.Items { + thisConfig := config + _, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(ctx, &thisConfig, metav1.CreateOptions{}) + require.NoError(t, err) + } + }) + + // Test that there is no default discovery endpoint available when there are no OIDCProviderConfigs. + requestNonExistentPath, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("http://%s/.well-known/openid-configuration", env.SupervisorAddress), + nil, + ) + require.NoError(t, err) + notFoundResponse, err := httpClient.Do(requestNonExistentPath) + require.NoError(t, err) + require.Equal(t, 404, notFoundResponse.StatusCode) + err = notFoundResponse.Body.Close() + require.NoError(t, err) + + // Create a new OIDCProviderConfig with a known issuer. + issuer := fmt.Sprintf("http://%s/nested/issuer", env.SupervisorAddress) + newOIDCProviderConfig := v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nested-issuser-config-from-integration-test", + Namespace: ns, + }, + Spec: v1alpha1.OIDCProviderConfigSpec{ + Issuer: issuer, + }, + } + _, err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(ctx, &newOIDCProviderConfig, metav1.CreateOptions{}) + require.NoError(t, err) + + // When this test has finished, clean up the new OIDCProviderConfig. + t.Cleanup(func() { + err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(ctx, newOIDCProviderConfig.Name, metav1.DeleteOptions{}) + require.NoError(t, err) + }) + + // Define a request to the new discovery endpoint which should have been created for the above OIDCProviderConfig. + requestDiscoveryEndpoint, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("http://%s/nested/issuer/.well-known/openid-configuration", env.SupervisorAddress), + nil, + ) + require.NoError(t, err) + + // Fetch that discovery endpoint. Give it some time for the endpoint to come into existence. + var response *http.Response + assert.Eventually(t, func() bool { + response, err = httpClient.Do(requestDiscoveryEndpoint) //nolint:bodyclose // the body is closed below after it is read + return err == nil + }, 10*time.Second, 200*time.Millisecond) + require.NoError(t, err) + responseBody, err := ioutil.ReadAll(response.Body) + require.NoError(t, err) + err = response.Body.Close() + require.NoError(t, err) + + // Check that the response matches our expectations. + expectedResultTemplate := here.Doc(`{ + "issuer": "%s", + "authorization_endpoint": "%s/connect/authorize", + "token_endpoint": "%s/connect/token", + "token_endpoint_auth_methods_supported": ["client_secret_basic"], + "token_endpoint_auth_signing_alg_values_supported": ["RS256"], + "jwks_uri": "%s/jwks.json", + "scopes_supported": ["openid", "offline"], + "response_types_supported": ["code"], + "claims_supported": ["groups"], + }`) + expectedJSON := fmt.Sprintf(expectedResultTemplate, issuer, issuer, issuer, issuer) + + require.Equal(t, 200, response.StatusCode) + require.Equal(t, "application/json", response.Header.Get("content-type")) + require.JSONEq(t, expectedJSON, string(responseBody)) } diff --git a/test/library/env.go b/test/library/env.go index c3479a374..257bd3219 100644 --- a/test/library/env.go +++ b/test/library/env.go @@ -25,11 +25,14 @@ const ( type TestEnv struct { t *testing.T - Namespace string `json:"namespace"` - AppName string `json:"appName"` - Capabilities map[TestClusterCapability]bool `json:"capabilities"` - TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"` - TestUser struct { + Namespace string `json:"namespace"` + SupervisorNamespace string `json:"supervisorNamespace"` + AppName string `json:"appName"` + SupervisorAppName string `json:"supervisorAppName"` + Capabilities map[TestClusterCapability]bool `json:"capabilities"` + TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"` + SupervisorAddress string `json:"supervisorAddress"` + TestUser struct { Token string `json:"token"` ExpectedUsername string `json:"expectedUsername"` ExpectedGroups []string `json:"expectedGroups"` @@ -71,6 +74,9 @@ func IntegrationEnv(t *testing.T) *TestEnv { result.TestUser.ExpectedGroups = strings.Split(strings.ReplaceAll(needEnv("PINNIPED_TEST_USER_GROUPS"), " ", ""), ",") result.TestUser.Token = needEnv("PINNIPED_TEST_USER_TOKEN") result.TestWebhook.Endpoint = needEnv("PINNIPED_TEST_WEBHOOK_ENDPOINT") + result.SupervisorNamespace = needEnv("PINNIPED_SUPERVISOR_NAMESPACE") + result.SupervisorAppName = needEnv("PINNIPED_SUPERVISOR_APP_NAME") + result.SupervisorAddress = needEnv("PINNIPED_TEST_SUPERVISOR_ADDRESS") result.TestWebhook.TLS = &idpv1alpha1.TLSSpec{CertificateAuthorityData: needEnv("PINNIPED_TEST_WEBHOOK_CA_BUNDLE")} result.t = t return &result From ead1ade24b8c70b9ef736200e7d93ddf9d78a52c Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 7 Oct 2020 10:50:55 -0400 Subject: [PATCH 08/28] supervisor-oidc: forgot OIDCProviderConfig type registration in 14f1d86 Signed-off-by: Andrew Keesler --- apis/config/v1alpha1/register.go.tmpl | 2 ++ generated/1.17/apis/config/v1alpha1/register.go | 2 ++ generated/1.18/apis/config/v1alpha1/register.go | 2 ++ generated/1.19/apis/config/v1alpha1/register.go | 2 ++ 4 files changed, 8 insertions(+) diff --git a/apis/config/v1alpha1/register.go.tmpl b/apis/config/v1alpha1/register.go.tmpl index 18936d7eb..7cd4b75d7 100644 --- a/apis/config/v1alpha1/register.go.tmpl +++ b/apis/config/v1alpha1/register.go.tmpl @@ -32,6 +32,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &CredentialIssuerConfig{}, &CredentialIssuerConfigList{}, + &OIDCProviderConfig{}, + &OIDCProviderConfigList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/generated/1.17/apis/config/v1alpha1/register.go b/generated/1.17/apis/config/v1alpha1/register.go index 18936d7eb..7cd4b75d7 100644 --- a/generated/1.17/apis/config/v1alpha1/register.go +++ b/generated/1.17/apis/config/v1alpha1/register.go @@ -32,6 +32,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &CredentialIssuerConfig{}, &CredentialIssuerConfigList{}, + &OIDCProviderConfig{}, + &OIDCProviderConfigList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/generated/1.18/apis/config/v1alpha1/register.go b/generated/1.18/apis/config/v1alpha1/register.go index 18936d7eb..7cd4b75d7 100644 --- a/generated/1.18/apis/config/v1alpha1/register.go +++ b/generated/1.18/apis/config/v1alpha1/register.go @@ -32,6 +32,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &CredentialIssuerConfig{}, &CredentialIssuerConfigList{}, + &OIDCProviderConfig{}, + &OIDCProviderConfigList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/generated/1.19/apis/config/v1alpha1/register.go b/generated/1.19/apis/config/v1alpha1/register.go index 18936d7eb..7cd4b75d7 100644 --- a/generated/1.19/apis/config/v1alpha1/register.go +++ b/generated/1.19/apis/config/v1alpha1/register.go @@ -32,6 +32,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &CredentialIssuerConfig{}, &CredentialIssuerConfigList{}, + &OIDCProviderConfig{}, + &OIDCProviderConfigList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil From 8a772793b8ab8dec2170ff4b8679cdbfebd7a647 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 7 Oct 2020 10:51:39 -0400 Subject: [PATCH 09/28] supervisor-oidc: fix PINNIPED_SUPERVISOR test env vars? Signed-off-by: Andrew Keesler --- hack/prepare-for-integration-tests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index ba39ad4f6..3a1a04fae 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -259,8 +259,8 @@ export PINNIPED_TEST_USER_GROUPS=${test_groups} export PINNIPED_TEST_USER_TOKEN=${test_username}:${test_password} export PINNIPED_TEST_WEBHOOK_ENDPOINT=${webhook_url} export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle} -export PINNIPED_SUPERVISOR_NAMESPACE=${namespace} -export PINNIPED_SUPERVISOR_APP_NAME=${app_name} +export PINNIPED_SUPERVISOR_NAMESPACE=${supervisor_namespace} +export PINNIPED_SUPERVISOR_APP_NAME=${supervisor_app_name} export PINNIPED_TEST_SUPERVISOR_ADDRESS="localhost:12345" read -r -d '' PINNIPED_CLUSTER_CAPABILITY_YAML << PINNIPED_CLUSTER_CAPABILITY_YAML_EOF || true From 019f44982c483400088166f5cda28b89acae9bc9 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 7 Oct 2020 10:53:05 -0400 Subject: [PATCH 10/28] supervisor-oidc: checkpoint: controller watches OIDCProviderConfig Signed-off-by: Andrew Keesler --- cmd/pinniped-supervisor/main.go | 56 ++++--------- deploy-supervisor/rbac.yaml | 4 +- .../dynamic_config_watcher.go | 72 ++++++++-------- internal/oidc/discovery/discovery.go | 24 +++--- internal/oidc/discovery/discovery_test.go | 42 +++++++--- .../oidc/issuerprovider/issuerprovider.go | 56 ++++++++++++- .../issuerprovider/issuerprovider_test.go | 84 +++++++++++++++++++ test/integration/supervisor_discovery_test.go | 20 ++++- 8 files changed, 249 insertions(+), 109 deletions(-) create mode 100644 internal/oidc/issuerprovider/issuerprovider_test.go diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index d6fa97acd..6e0866338 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -6,22 +6,20 @@ package main import ( "context" "fmt" - "io/ioutil" "net" "net/http" "os" "os/signal" "time" - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" "k8s.io/client-go/pkg/version" "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest" "k8s.io/component-base/logs" "k8s.io/klog/v2" - "sigs.k8s.io/yaml" + pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned" + pinnipedinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions" "go.pinniped.dev/internal/controller/supervisorconfig" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/downward" @@ -69,63 +67,57 @@ func waitForSignal() os.Signal { func startControllers( ctx context.Context, issuerProvider *issuerprovider.Provider, - kubeClient kubernetes.Interface, - kubeInformers kubeinformers.SharedInformerFactory, - serverInstallationNamespace string, - staticConfig StaticConfig, + pinnipedInformers pinnipedinformers.SharedInformerFactory, ) { // Create controller manager. controllerManager := controllerlib. NewManager(). WithController( supervisorconfig.NewDynamicConfigWatcherController( - serverInstallationNamespace, - staticConfig.NamesConfig.DynamicConfigMap, issuerProvider, - kubeClient, - kubeInformers.Core().V1().ConfigMaps(), + pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(), controllerlib.WithInformer, ), singletonWorker, ) - kubeInformers.Start(ctx.Done()) + pinnipedInformers.Start(ctx.Done()) go controllerManager.Start(ctx) } -func newK8sClient() (kubernetes.Interface, error) { +func newPinnipedClient() (pinnipedclientset.Interface, error) { kubeConfig, err := restclient.InClusterConfig() if err != nil { return nil, fmt.Errorf("could not load in-cluster configuration: %w", err) } // Connect to the core Kubernetes API. - kubeClient, err := kubernetes.NewForConfig(kubeConfig) + pinnipedClient, err := pinnipedclientset.NewForConfig(kubeConfig) if err != nil { return nil, fmt.Errorf("could not load in-cluster configuration: %w", err) } - return kubeClient, nil + return pinnipedClient, nil } -func run(serverInstallationNamespace string, staticConfig StaticConfig) error { +func run(serverInstallationNamespace string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - kubeClient, err := newK8sClient() + pinnipedClient, err := newPinnipedClient() if err != nil { return fmt.Errorf("cannot create k8s client: %w", err) } - kubeInformers := kubeinformers.NewSharedInformerFactoryWithOptions( - kubeClient, + pinnipedInformers := pinnipedinformers.NewSharedInformerFactoryWithOptions( + pinnipedClient, defaultResyncInterval, - kubeinformers.WithNamespace(serverInstallationNamespace), + pinnipedinformers.WithNamespace(serverInstallationNamespace), ) issuerProvider := issuerprovider.New() - startControllers(ctx, issuerProvider, kubeClient, kubeInformers, serverInstallationNamespace, staticConfig) + startControllers(ctx, issuerProvider, pinnipedInformers) //nolint: gosec // Intentionally binding to all network interfaces. l, err := net.Listen("tcp", ":80") @@ -143,14 +135,6 @@ func run(serverInstallationNamespace string, staticConfig StaticConfig) error { return nil } -type StaticConfig struct { - NamesConfig NamesConfigSpec `json:"names"` -} - -type NamesConfigSpec struct { - DynamicConfigMap string `json:"dynamicConfigMap"` -} - func main() { logs.InitLogs() defer logs.FlushLogs() @@ -164,17 +148,7 @@ func main() { klog.Fatal(fmt.Errorf("could not read pod metadata: %w", err)) } - // Read static config. - data, err := ioutil.ReadFile(os.Args[2]) - if err != nil { - klog.Fatal(fmt.Errorf("read file: %w", err)) - } - var staticConfig StaticConfig - if err := yaml.Unmarshal(data, &staticConfig); err != nil { - klog.Fatal(fmt.Errorf("decode yaml: %w", err)) - } - - if err := run(podInfo.Namespace, staticConfig); err != nil { + if err := run(podInfo.Namespace); err != nil { klog.Fatal(err) } } diff --git a/deploy-supervisor/rbac.yaml b/deploy-supervisor/rbac.yaml index ecba850b2..a4b34e9a6 100644 --- a/deploy-supervisor/rbac.yaml +++ b/deploy-supervisor/rbac.yaml @@ -13,8 +13,8 @@ metadata: labels: app: #@ data.values.app_name rules: - - apiGroups: [""] - resources: [configmaps] + - apiGroups: [config.pinniped.dev] + resources: [oidcproviderconfigs] verbs: [get, list, watch] --- kind: RoleBinding diff --git a/internal/controller/supervisorconfig/dynamic_config_watcher.go b/internal/controller/supervisorconfig/dynamic_config_watcher.go index d1aa8e710..a060e4e99 100644 --- a/internal/controller/supervisorconfig/dynamic_config_watcher.go +++ b/internal/controller/supervisorconfig/dynamic_config_watcher.go @@ -5,12 +5,12 @@ package supervisorconfig import ( "fmt" + "net/url" k8serrors "k8s.io/apimachinery/pkg/api/errors" - corev1informers "k8s.io/client-go/informers/core/v1" - "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" + configinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions/config/v1alpha1" pinnipedcontroller "go.pinniped.dev/internal/controller" "go.pinniped.dev/internal/controllerlib" ) @@ -22,41 +22,36 @@ const ( // IssuerSetter can be notified of a valid issuer with its SetIssuer function. If there is no // longer any valid issuer, then nil can be passed to this interface. // +// If the IssuerSetter doesn't like the provided issuer, it can return an error. +// // Implementations of this type should be thread-safe to support calls from multiple goroutines. type IssuerSetter interface { - SetIssuer(issuer *string) + SetIssuer(issuer *url.URL) error } type dynamicConfigWatcherController struct { - configMapName string - configMapNamespace string - issuerSetter IssuerSetter - k8sClient kubernetes.Interface - configMapInformer corev1informers.ConfigMapInformer + issuerSetter IssuerSetter + opcInformer configinformers.OIDCProviderConfigInformer } +// NewDynamicConfigWatcherController creates a controllerlib.Controller that watches +// OIDCProviderConfig objects and notifies a callback object of their creation or deletion. func NewDynamicConfigWatcherController( - serverInstallationNamespace string, - configMapName string, issuerObserver IssuerSetter, - k8sClient kubernetes.Interface, - configMapInformer corev1informers.ConfigMapInformer, + opcInformer configinformers.OIDCProviderConfigInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { return controllerlib.New( controllerlib.Config{ Name: "DynamicConfigWatcherController", Syncer: &dynamicConfigWatcherController{ - configMapNamespace: serverInstallationNamespace, - configMapName: configMapName, - issuerSetter: issuerObserver, - k8sClient: k8sClient, - configMapInformer: configMapInformer, + issuerSetter: issuerObserver, + opcInformer: opcInformer, }, }, withInformer( - configMapInformer, - pinnipedcontroller.NameAndNamespaceExactMatchFilterFactory(configMapName, serverInstallationNamespace), + opcInformer, + pinnipedcontroller.NoOpFilter(), controllerlib.InformerOption{}, ), ) @@ -69,44 +64,49 @@ func (c *dynamicConfigWatcherController) Sync(ctx controllerlib.Context) error { // TODO The discovery endpoint would return an error until all missing configuration options are // filled in. - configMap, err := c.configMapInformer. + opc, err := c.opcInformer. Lister(). - ConfigMaps(c.configMapNamespace). - Get(c.configMapName) + OIDCProviderConfigs(ctx.Key.Namespace). + Get(ctx.Key.Name) notFound := k8serrors.IsNotFound(err) if err != nil && !notFound { - return fmt.Errorf("failed to get %s/%s secret: %w", c.configMapNamespace, c.configMapName, err) + return fmt.Errorf("failed to get %s/%s oidcproviderconfig: %w", ctx.Key.Namespace, ctx.Key.Name, err) } if notFound { klog.InfoS( - "dynamicConfigWatcherController Sync found no configmap", - "configmap", - klog.KRef(c.configMapNamespace, c.configMapName), + "dynamicConfigWatcherController Sync found no oidcproviderconfig", + "oidcproviderconfig", + klog.KRef(ctx.Key.Namespace, ctx.Key.Name), ) c.issuerSetter.SetIssuer(nil) return nil } - issuer, ok := configMap.Data[issuerConfigMapKey] - if !ok { + url, err := url.Parse(opc.Spec.Issuer) + if err != nil { klog.InfoS( - "dynamicConfigWatcherController Sync found no issuer", - "configmap", - klog.KObj(configMap), + "dynamicConfigWatcherController Sync failed to parse issuer", + "err", + err, ) - c.issuerSetter.SetIssuer(nil) return nil } klog.InfoS( "dynamicConfigWatcherController Sync issuer", - "configmap", - klog.KObj(configMap), + "oidcproviderconfig", + klog.KObj(opc), "issuer", - issuer, + url, ) - c.issuerSetter.SetIssuer(&issuer) + if err := c.issuerSetter.SetIssuer(url); err != nil { + klog.InfoS( + "dynamicConfigWatcherController Sync failed to set issuer", + "err", + err, + ) + } return nil } diff --git a/internal/oidc/discovery/discovery.go b/internal/oidc/discovery/discovery.go index a65f8c8db..96f858f51 100644 --- a/internal/oidc/discovery/discovery.go +++ b/internal/oidc/discovery/discovery.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" ) // Metadata holds all fields (that we care about) from the OpenID Provider Metadata section in the @@ -30,7 +31,7 @@ type Metadata struct { // // Implementations of this type should be thread-safe to support calls from multiple goroutines. type IssuerGetter interface { - GetIssuer() *string + GetIssuer() *url.URL } // New returns an http.Handler that will use information from the provided IssuerGetter to serve an @@ -39,22 +40,23 @@ func New(ig IssuerGetter) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") + issuer := ig.GetIssuer() + if issuer == nil { + http.Error(w, `{"error": "OIDC discovery not available (unknown issuer)"}`, http.StatusNotFound) + return + } + if r.Method != http.MethodGet { http.Error(w, `{"error": "Method not allowed (try GET)"}`, http.StatusMethodNotAllowed) return } - issuer := ig.GetIssuer() - if issuer == nil { - http.Error(w, `{"error": "OIDC discovery not available (unknown issuer)"}`, http.StatusServiceUnavailable) - return - } - + issuerURL := issuer.String() oidcConfig := Metadata{ - Issuer: *issuer, - AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", *issuer), - TokenEndpoint: fmt.Sprintf("%s/oauth2/v0/token", *issuer), - JWKSURL: fmt.Sprintf("%s/oauth2/v0/keys", *issuer), + Issuer: issuerURL, + AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", issuerURL), + TokenEndpoint: fmt.Sprintf("%s/oauth2/v0/token", issuerURL), + JWKSURL: fmt.Sprintf("%s/oauth2/v0/keys", issuerURL), ResponseTypesSupported: []string{}, SubjectTypesSupported: []string{}, IDTokenSigningAlgValuesSupported: []string{}, diff --git a/internal/oidc/discovery/discovery_test.go b/internal/oidc/discovery/discovery_test.go index b89aab977..47fab0701 100644 --- a/internal/oidc/discovery/discovery_test.go +++ b/internal/oidc/discovery/discovery_test.go @@ -7,6 +7,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/stretchr/testify/require" @@ -18,7 +19,7 @@ func TestDiscovery(t *testing.T) { tests := []struct { name string - issuer string + issuer *url.URL method string wantStatus int @@ -26,16 +27,16 @@ func TestDiscovery(t *testing.T) { wantBody interface{} }{ { - name: "issuer returns nil issuer", + name: "nil issuer", method: http.MethodGet, - wantStatus: http.StatusServiceUnavailable, + wantStatus: http.StatusNotFound, wantBody: map[string]string{ "error": "OIDC discovery not available (unknown issuer)", }, }, { - name: "issuer returns non-nil issuer", - issuer: "https://some-issuer.com", + name: "issuer without path", + issuer: must(url.Parse("https://some-issuer.com")), method: http.MethodGet, wantStatus: http.StatusOK, wantContentType: "application/json", @@ -49,9 +50,25 @@ func TestDiscovery(t *testing.T) { IDTokenSigningAlgValuesSupported: []string{}, }, }, + { + name: "issuer with path", + issuer: must(url.Parse("https://some-issuer.com/some/path")), + method: http.MethodGet, + wantStatus: http.StatusOK, + wantContentType: "application/json", + wantBody: &Metadata{ + Issuer: "https://some-issuer.com/some/path", + AuthorizationEndpoint: "https://some-issuer.com/some/path/oauth2/v0/auth", + TokenEndpoint: "https://some-issuer.com/some/path/oauth2/v0/token", + JWKSURL: "https://some-issuer.com/some/path/oauth2/v0/keys", + ResponseTypesSupported: []string{}, + SubjectTypesSupported: []string{}, + IDTokenSigningAlgValuesSupported: []string{}, + }, + }, { name: "bad method", - issuer: "https://some-issuer.com", + issuer: must(url.Parse("https://some-issuer.com")), method: http.MethodPost, wantStatus: http.StatusMethodNotAllowed, wantBody: map[string]string{ @@ -63,11 +80,7 @@ func TestDiscovery(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { p := issuerprovider.New() - if test.issuer != "" { - p.SetIssuer(&test.issuer) - } else { - p.SetIssuer(nil) - } + p.SetIssuer(test.issuer) handler := New(p) req := httptest.NewRequest(test.method, "/this/path/shouldnt/matter", nil) @@ -88,3 +101,10 @@ func TestDiscovery(t *testing.T) { }) } } + +func must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + return u +} diff --git a/internal/oidc/issuerprovider/issuerprovider.go b/internal/oidc/issuerprovider/issuerprovider.go index 248258601..36ec8c59d 100644 --- a/internal/oidc/issuerprovider/issuerprovider.go +++ b/internal/oidc/issuerprovider/issuerprovider.go @@ -4,14 +4,20 @@ // Package issuerprovider provides a thread-safe type that can hold on to an OIDC issuer name. package issuerprovider -import "sync" +import ( + "net/url" + "strings" + "sync" + + "go.pinniped.dev/internal/constable" +) // Provider is a type that can hold onto an issuer value, which may be nil. // // It is thread-safe. type Provider struct { mu sync.RWMutex - issuer *string + issuer *url.URL } // New returns an empty Provider, i.e., one that holds a nil issuer. @@ -19,14 +25,56 @@ func New() *Provider { return &Provider{} } -func (p *Provider) SetIssuer(issuer *string) { +// SetIssuer validates and sets the provided issuer. If validation fails, SetIssuer will return +// an error. +func (p *Provider) SetIssuer(issuer *url.URL) error { + if err := p.validateIssuer(issuer); err != nil { + return err + } + p.setIssuer(issuer) + return nil +} + +func (p *Provider) validateIssuer(issuer *url.URL) error { + if issuer == nil { + return nil + } + + if issuer.Scheme != "https" && removeMeAfterWeNoLongerNeedHTTPIssuerSupport(issuer.Scheme) { + return constable.Error(`issuer must have "https" scheme`) + } + + if issuer.User != nil { + return constable.Error(`issuer must not have username or password`) + } + + if strings.HasSuffix(issuer.Path, "/") { + return constable.Error(`issuer must not have trailing slash in path`) + } + + if issuer.RawQuery != "" { + return constable.Error(`issuer must not have query`) + } + + if issuer.Fragment != "" { + return constable.Error(`issuer must not have fragment`) + } + + return nil +} + +func (p *Provider) setIssuer(issuer *url.URL) { p.mu.Lock() defer p.mu.Unlock() p.issuer = issuer } -func (p *Provider) GetIssuer() *string { +func (p *Provider) GetIssuer() *url.URL { p.mu.RLock() defer p.mu.RUnlock() return p.issuer } + +func removeMeAfterWeNoLongerNeedHTTPIssuerSupport(scheme string) bool { + return scheme != "http" +} diff --git a/internal/oidc/issuerprovider/issuerprovider_test.go b/internal/oidc/issuerprovider/issuerprovider_test.go new file mode 100644 index 000000000..356b338b5 --- /dev/null +++ b/internal/oidc/issuerprovider/issuerprovider_test.go @@ -0,0 +1,84 @@ +package issuerprovider + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestProvider(t *testing.T) { + tests := []struct { + name string + issuer *url.URL + wantError string + }{ + { + name: "nil issuer", + issuer: nil, + }, + { + name: "no scheme", + issuer: must(url.Parse("tuna.com")), + wantError: `issuer must have "https" scheme`, + }, + { + name: "bad scheme", + issuer: must(url.Parse("ftp://tuna.com")), + wantError: `issuer must have "https" scheme`, + }, + { + name: "fragment", + issuer: must(url.Parse("https://tuna.com/fish#some-frag")), + wantError: `issuer must not have fragment`, + }, + { + name: "query", + issuer: must(url.Parse("https://tuna.com?some=query")), + wantError: `issuer must not have query`, + }, + { + name: "username", + issuer: must(url.Parse("https://username@tuna.com")), + wantError: `issuer must not have username or password`, + }, + { + name: "password", + issuer: must(url.Parse("https://username:password@tuna.com")), + wantError: `issuer must not have username or password`, + }, + { + name: "without path", + issuer: must(url.Parse("https://tuna.com")), + }, + { + name: "with path", + issuer: must(url.Parse("https://tuna.com/fish/marlin")), + }, + { + name: "trailing slash in path", + issuer: must(url.Parse("https://tuna.com/")), + wantError: `issuer must not have trailing slash in path`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := New() + err := p.SetIssuer(tt.issuer) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + require.Nil(t, p.GetIssuer()) + } else { + require.NoError(t, err) + require.Equal(t, tt.issuer, p.GetIssuer()) + } + }) + } +} + +func must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + return u +} diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 6c5cce780..d0456ce07 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -40,9 +40,13 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { // When this test has finished, recreate any OIDCProviderConfigs that had existed on the cluster before this test. t.Cleanup(func() { + cleanupCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + for _, config := range originalConfigList.Items { thisConfig := config - _, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(ctx, &thisConfig, metav1.CreateOptions{}) + thisConfig.ResourceVersion = "" // Get rid of resource version since we can't create an object with one. + _, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(cleanupCtx, &thisConfig, metav1.CreateOptions{}) require.NoError(t, err) } }) @@ -64,6 +68,10 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { // Create a new OIDCProviderConfig with a known issuer. issuer := fmt.Sprintf("http://%s/nested/issuer", env.SupervisorAddress) newOIDCProviderConfig := v1alpha1.OIDCProviderConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "OIDCProviderConfig", + APIVersion: v1alpha1.SchemeGroupVersion.String(), + }, ObjectMeta: metav1.ObjectMeta{ Name: "nested-issuser-config-from-integration-test", Namespace: ns, @@ -77,7 +85,10 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { // When this test has finished, clean up the new OIDCProviderConfig. t.Cleanup(func() { - err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(ctx, newOIDCProviderConfig.Name, metav1.DeleteOptions{}) + cleanupCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(cleanupCtx, newOIDCProviderConfig.Name, metav1.DeleteOptions{}) require.NoError(t, err) }) @@ -94,9 +105,11 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { var response *http.Response assert.Eventually(t, func() bool { response, err = httpClient.Do(requestDiscoveryEndpoint) //nolint:bodyclose // the body is closed below after it is read - return err == nil + return err == nil && response.StatusCode == http.StatusOK }, 10*time.Second, 200*time.Millisecond) require.NoError(t, err) + require.Equal(t, http.StatusOK, response.StatusCode) + responseBody, err := ioutil.ReadAll(response.Body) require.NoError(t, err) err = response.Body.Close() @@ -116,7 +129,6 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { }`) expectedJSON := fmt.Sprintf(expectedResultTemplate, issuer, issuer, issuer, issuer) - require.Equal(t, 200, response.StatusCode) require.Equal(t, "application/json", response.Header.Get("content-type")) require.JSONEq(t, expectedJSON, string(responseBody)) } From c49ebf4b57ff0925e0ca33c33967e5b52ce738f6 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 7 Oct 2020 11:33:50 -0400 Subject: [PATCH 11/28] supervisor-oidc: int test passes, but impl needs refactor Signed-off-by: Andrew Keesler --- cmd/pinniped-supervisor/main.go | 5 +- internal/oidc/discovery/discovery.go | 37 +++++++++--- internal/oidc/discovery/discovery_test.go | 57 ++++++++++++------- test/integration/supervisor_discovery_test.go | 6 +- 4 files changed, 68 insertions(+), 37 deletions(-) diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index 6e0866338..96758ba25 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -23,7 +23,6 @@ import ( "go.pinniped.dev/internal/controller/supervisorconfig" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/downward" - "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc/discovery" "go.pinniped.dev/internal/oidc/issuerprovider" ) @@ -34,10 +33,8 @@ const ( ) func start(ctx context.Context, l net.Listener, discoveryHandler http.Handler) { - mux := http.NewServeMux() - mux.Handle(oidc.WellKnownURLPath, discoveryHandler) server := http.Server{ - Handler: mux, + Handler: discoveryHandler, } errCh := make(chan error) diff --git a/internal/oidc/discovery/discovery.go b/internal/oidc/discovery/discovery.go index 96f858f51..2293e9637 100644 --- a/internal/oidc/discovery/discovery.go +++ b/internal/oidc/discovery/discovery.go @@ -9,21 +9,36 @@ import ( "fmt" "net/http" "net/url" + + "go.pinniped.dev/internal/oidc" ) // Metadata holds all fields (that we care about) from the OpenID Provider Metadata section in the // OpenID Connect Discovery specification: // https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3. type Metadata struct { + // vvvRequiredvvv + Issuer string `json:"issuer"` AuthorizationEndpoint string `json:"authorization_endpoint"` TokenEndpoint string `json:"token_endpoint"` - JWKSURL string `json:"jwks_url"` + JWKSURI string `json:"jwks_uri"` ResponseTypesSupported []string `json:"response_types_supported"` SubjectTypesSupported []string `json:"subject_types_supported"` IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"` + + // ^^^Required^^^ + + // vvvOptionalvvv + + TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"` + TokenEndpointAuthSigningAlgoValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"` + ScopesSupported []string `json:"scopes_supported"` + ClaimsSupported []string `json:"claims_supported"` + + // ^^^Optional^^^ } // IssuerGetter holds onto an issuer which can be retrieved via its GetIssuer function. If there is @@ -41,7 +56,7 @@ func New(ig IssuerGetter) http.Handler { w.Header().Set("Content-Type", "application/json") issuer := ig.GetIssuer() - if issuer == nil { + if issuer == nil || r.URL.Path != issuer.Path+oidc.WellKnownURLPath { http.Error(w, `{"error": "OIDC discovery not available (unknown issuer)"}`, http.StatusNotFound) return } @@ -53,13 +68,17 @@ func New(ig IssuerGetter) http.Handler { issuerURL := issuer.String() oidcConfig := Metadata{ - Issuer: issuerURL, - AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", issuerURL), - TokenEndpoint: fmt.Sprintf("%s/oauth2/v0/token", issuerURL), - JWKSURL: fmt.Sprintf("%s/oauth2/v0/keys", issuerURL), - ResponseTypesSupported: []string{}, - SubjectTypesSupported: []string{}, - IDTokenSigningAlgValuesSupported: []string{}, + Issuer: issuerURL, + AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", issuerURL), + TokenEndpoint: fmt.Sprintf("%s/oauth2/v0/token", issuerURL), + JWKSURI: fmt.Sprintf("%s/jwks.json", issuerURL), + ResponseTypesSupported: []string{"code"}, + SubjectTypesSupported: []string{"public"}, + IDTokenSigningAlgValuesSupported: []string{"RS256"}, + TokenEndpointAuthMethodsSupported: []string{"client_secret_basic"}, + TokenEndpointAuthSigningAlgoValuesSupported: []string{"RS256"}, + ScopesSupported: []string{"openid", "offline"}, + ClaimsSupported: []string{"groups"}, } if err := json.NewEncoder(w).Encode(&oidcConfig); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/internal/oidc/discovery/discovery_test.go b/internal/oidc/discovery/discovery_test.go index 47fab0701..7d1c481d2 100644 --- a/internal/oidc/discovery/discovery_test.go +++ b/internal/oidc/discovery/discovery_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" + "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc/issuerprovider" ) @@ -21,6 +22,7 @@ func TestDiscovery(t *testing.T) { issuer *url.URL method string + path string wantStatus int wantContentType string @@ -29,47 +31,58 @@ func TestDiscovery(t *testing.T) { { name: "nil issuer", method: http.MethodGet, + path: oidc.WellKnownURLPath, wantStatus: http.StatusNotFound, wantBody: map[string]string{ "error": "OIDC discovery not available (unknown issuer)", }, }, { - name: "issuer without path", - issuer: must(url.Parse("https://some-issuer.com")), - method: http.MethodGet, - wantStatus: http.StatusOK, - wantContentType: "application/json", - wantBody: &Metadata{ - Issuer: "https://some-issuer.com", - AuthorizationEndpoint: "https://some-issuer.com/oauth2/v0/auth", - TokenEndpoint: "https://some-issuer.com/oauth2/v0/token", - JWKSURL: "https://some-issuer.com/oauth2/v0/keys", - ResponseTypesSupported: []string{}, - SubjectTypesSupported: []string{}, - IDTokenSigningAlgValuesSupported: []string{}, + name: "root path mismatch", + issuer: must(url.Parse("https://some-issuer.com/some/path")), + method: http.MethodGet, + path: "/some/other/path" + oidc.WellKnownURLPath, + wantStatus: http.StatusNotFound, + wantBody: map[string]string{ + "error": "OIDC discovery not available (unknown issuer)", }, }, { - name: "issuer with path", + name: "well-known path mismatch", + issuer: must(url.Parse("https://some-issuer.com/some/path")), + method: http.MethodGet, + path: "/some/path/that/is/not/the/well-known/path", + wantStatus: http.StatusNotFound, + wantBody: map[string]string{ + "error": "OIDC discovery not available (unknown issuer)", + }, + }, + { + name: "issuer path matches", issuer: must(url.Parse("https://some-issuer.com/some/path")), method: http.MethodGet, + path: "/some/path" + oidc.WellKnownURLPath, wantStatus: http.StatusOK, wantContentType: "application/json", wantBody: &Metadata{ - Issuer: "https://some-issuer.com/some/path", - AuthorizationEndpoint: "https://some-issuer.com/some/path/oauth2/v0/auth", - TokenEndpoint: "https://some-issuer.com/some/path/oauth2/v0/token", - JWKSURL: "https://some-issuer.com/some/path/oauth2/v0/keys", - ResponseTypesSupported: []string{}, - SubjectTypesSupported: []string{}, - IDTokenSigningAlgValuesSupported: []string{}, + Issuer: "https://some-issuer.com/some/path", + AuthorizationEndpoint: "https://some-issuer.com/some/path/oauth2/v0/auth", + TokenEndpoint: "https://some-issuer.com/some/path/oauth2/v0/token", + JWKSURI: "https://some-issuer.com/some/path/jwks.json", + ResponseTypesSupported: []string{"code"}, + SubjectTypesSupported: []string{"public"}, + IDTokenSigningAlgValuesSupported: []string{"RS256"}, + TokenEndpointAuthMethodsSupported: []string{"client_secret_basic"}, + TokenEndpointAuthSigningAlgoValuesSupported: []string{"RS256"}, + ScopesSupported: []string{"openid", "offline"}, + ClaimsSupported: []string{"groups"}, }, }, { name: "bad method", issuer: must(url.Parse("https://some-issuer.com")), method: http.MethodPost, + path: oidc.WellKnownURLPath, wantStatus: http.StatusMethodNotAllowed, wantBody: map[string]string{ "error": "Method not allowed (try GET)", @@ -83,7 +96,7 @@ func TestDiscovery(t *testing.T) { p.SetIssuer(test.issuer) handler := New(p) - req := httptest.NewRequest(test.method, "/this/path/shouldnt/matter", nil) + req := httptest.NewRequest(test.method, test.path, nil) rsp := httptest.NewRecorder() handler.ServeHTTP(rsp, req) diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index d0456ce07..d2e0bca29 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -118,14 +118,16 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { // Check that the response matches our expectations. expectedResultTemplate := here.Doc(`{ "issuer": "%s", - "authorization_endpoint": "%s/connect/authorize", - "token_endpoint": "%s/connect/token", + "authorization_endpoint": "%s/oauth2/v0/auth", + "token_endpoint": "%s/oauth2/v0/token", "token_endpoint_auth_methods_supported": ["client_secret_basic"], "token_endpoint_auth_signing_alg_values_supported": ["RS256"], "jwks_uri": "%s/jwks.json", "scopes_supported": ["openid", "offline"], "response_types_supported": ["code"], "claims_supported": ["groups"], + "subject_types_supported": ["public"], + "id_token_signing_alg_values_supported": ["RS256"] }`) expectedJSON := fmt.Sprintf(expectedResultTemplate, issuer, issuer, issuer, issuer) From f48a4e445e8c5b31a3925cdc2e77c51ee10cc533 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 7 Oct 2020 11:48:21 -0400 Subject: [PATCH 12/28] Fix linting and unit tests Signed-off-by: Andrew Keesler --- .../supervisorconfig/dynamic_config_watcher.go | 12 +++++++----- internal/oidc/discovery/discovery_test.go | 3 ++- internal/oidc/issuerprovider/issuerprovider_test.go | 4 ++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/internal/controller/supervisorconfig/dynamic_config_watcher.go b/internal/controller/supervisorconfig/dynamic_config_watcher.go index a060e4e99..085475b14 100644 --- a/internal/controller/supervisorconfig/dynamic_config_watcher.go +++ b/internal/controller/supervisorconfig/dynamic_config_watcher.go @@ -15,10 +15,6 @@ import ( "go.pinniped.dev/internal/controllerlib" ) -const ( - issuerConfigMapKey = "issuer" -) - // IssuerSetter can be notified of a valid issuer with its SetIssuer function. If there is no // longer any valid issuer, then nil can be passed to this interface. // @@ -79,7 +75,13 @@ func (c *dynamicConfigWatcherController) Sync(ctx controllerlib.Context) error { "oidcproviderconfig", klog.KRef(ctx.Key.Namespace, ctx.Key.Name), ) - c.issuerSetter.SetIssuer(nil) + if err := c.issuerSetter.SetIssuer(nil); err != nil { + klog.InfoS( + "dynamicConfigWatcherController Sync failed to set issuer", + "err", + err, + ) + } return nil } diff --git a/internal/oidc/discovery/discovery_test.go b/internal/oidc/discovery/discovery_test.go index 7d1c481d2..49f46bebc 100644 --- a/internal/oidc/discovery/discovery_test.go +++ b/internal/oidc/discovery/discovery_test.go @@ -93,7 +93,8 @@ func TestDiscovery(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { p := issuerprovider.New() - p.SetIssuer(test.issuer) + err := p.SetIssuer(test.issuer) + require.NoError(t, err) handler := New(p) req := httptest.NewRequest(test.method, test.path, nil) diff --git a/internal/oidc/issuerprovider/issuerprovider_test.go b/internal/oidc/issuerprovider/issuerprovider_test.go index 356b338b5..128502f43 100644 --- a/internal/oidc/issuerprovider/issuerprovider_test.go +++ b/internal/oidc/issuerprovider/issuerprovider_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + package issuerprovider import ( @@ -62,6 +65,7 @@ func TestProvider(t *testing.T) { }, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { p := New() err := p.SetIssuer(tt.issuer) From 154de991e402f8f19af1173de7854367d66908df Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 7 Oct 2020 11:42:30 -0700 Subject: [PATCH 13/28] Make concierge_api_discovery_test.go less sensitive to order in a list Signed-off-by: Ryan Richard --- test/integration/concierge_api_discovery_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/concierge_api_discovery_test.go b/test/integration/concierge_api_discovery_test.go index b1ff45a97..b1c28cf68 100644 --- a/test/integration/concierge_api_discovery_test.go +++ b/test/integration/concierge_api_discovery_test.go @@ -139,7 +139,7 @@ func TestGetAPIResourceList(t *testing.T) { for i := range actualResourceList.APIResources { actualResourceList.APIResources[i].StorageVersionHash = "" } - require.EqualValues(t, expectedResources, actualResourceList.APIResources, "unexpected API resources") + require.ElementsMatch(t, expectedResources, actualResourceList.APIResources, "unexpected API resources") } }) } From 6b653fc663f811aa2823512b2202750928dde9e4 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 7 Oct 2020 19:18:34 -0700 Subject: [PATCH 14/28] Creation and deletion of OIDC Provider discovery endpoints from config - The OIDCProviderConfigWatcherController synchronizes the OIDCProviderConfig settings to dynamically mount and unmount the OIDC discovery endpoints for each provider - Integration test passes but unit tests need to be added still --- cmd/pinniped-supervisor/main.go | 19 +-- .../dynamic_config_watcher.go | 114 -------------- .../oidcproviderconfig_watcher.go | 91 +++++++++++ internal/oidc/discovery/discovery.go | 31 +--- internal/oidc/discovery/discovery_test.go | 52 +------ .../oidc/issuerprovider/issuerprovider.go | 80 ---------- internal/oidc/provider/manager.go | 115 ++++++++++++++ internal/oidc/provider/oidcprovider.go | 50 +++++++ .../oidcprovider_test.go} | 15 +- test/integration/supervisor_discovery_test.go | 141 +++++++++++++----- 10 files changed, 388 insertions(+), 320 deletions(-) delete mode 100644 internal/controller/supervisorconfig/dynamic_config_watcher.go create mode 100644 internal/controller/supervisorconfig/oidcproviderconfig_watcher.go delete mode 100644 internal/oidc/issuerprovider/issuerprovider.go create mode 100644 internal/oidc/provider/manager.go create mode 100644 internal/oidc/provider/oidcprovider.go rename internal/oidc/{issuerprovider/issuerprovider_test.go => provider/oidcprovider_test.go} (88%) diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index 96758ba25..9b111f50a 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -23,8 +23,7 @@ import ( "go.pinniped.dev/internal/controller/supervisorconfig" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/downward" - "go.pinniped.dev/internal/oidc/discovery" - "go.pinniped.dev/internal/oidc/issuerprovider" + "go.pinniped.dev/internal/oidc/provider" ) const ( @@ -32,10 +31,8 @@ const ( defaultResyncInterval = 3 * time.Minute ) -func start(ctx context.Context, l net.Listener, discoveryHandler http.Handler) { - server := http.Server{ - Handler: discoveryHandler, - } +func start(ctx context.Context, l net.Listener, handler http.Handler) { + server := http.Server{Handler: handler} errCh := make(chan error) go func() { @@ -63,14 +60,14 @@ func waitForSignal() os.Signal { func startControllers( ctx context.Context, - issuerProvider *issuerprovider.Provider, + issuerProvider *provider.Manager, pinnipedInformers pinnipedinformers.SharedInformerFactory, ) { // Create controller manager. controllerManager := controllerlib. NewManager(). WithController( - supervisorconfig.NewDynamicConfigWatcherController( + supervisorconfig.NewOIDCProviderConfigWatcherController( issuerProvider, pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(), controllerlib.WithInformer, @@ -113,8 +110,8 @@ func run(serverInstallationNamespace string) error { pinnipedinformers.WithNamespace(serverInstallationNamespace), ) - issuerProvider := issuerprovider.New() - startControllers(ctx, issuerProvider, pinnipedInformers) + oidProvidersManager := provider.NewManager(http.NotFoundHandler()) + startControllers(ctx, oidProvidersManager, pinnipedInformers) //nolint: gosec // Intentionally binding to all network interfaces. l, err := net.Listen("tcp", ":80") @@ -123,7 +120,7 @@ func run(serverInstallationNamespace string) error { } defer l.Close() - start(ctx, l, discovery.New(issuerProvider)) + start(ctx, l, oidProvidersManager) klog.InfoS("supervisor is ready", "address", l.Addr().String()) gotSignal := waitForSignal() diff --git a/internal/controller/supervisorconfig/dynamic_config_watcher.go b/internal/controller/supervisorconfig/dynamic_config_watcher.go deleted file mode 100644 index 085475b14..000000000 --- a/internal/controller/supervisorconfig/dynamic_config_watcher.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package supervisorconfig - -import ( - "fmt" - "net/url" - - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/klog/v2" - - configinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions/config/v1alpha1" - pinnipedcontroller "go.pinniped.dev/internal/controller" - "go.pinniped.dev/internal/controllerlib" -) - -// IssuerSetter can be notified of a valid issuer with its SetIssuer function. If there is no -// longer any valid issuer, then nil can be passed to this interface. -// -// If the IssuerSetter doesn't like the provided issuer, it can return an error. -// -// Implementations of this type should be thread-safe to support calls from multiple goroutines. -type IssuerSetter interface { - SetIssuer(issuer *url.URL) error -} - -type dynamicConfigWatcherController struct { - issuerSetter IssuerSetter - opcInformer configinformers.OIDCProviderConfigInformer -} - -// NewDynamicConfigWatcherController creates a controllerlib.Controller that watches -// OIDCProviderConfig objects and notifies a callback object of their creation or deletion. -func NewDynamicConfigWatcherController( - issuerObserver IssuerSetter, - opcInformer configinformers.OIDCProviderConfigInformer, - withInformer pinnipedcontroller.WithInformerOptionFunc, -) controllerlib.Controller { - return controllerlib.New( - controllerlib.Config{ - Name: "DynamicConfigWatcherController", - Syncer: &dynamicConfigWatcherController{ - issuerSetter: issuerObserver, - opcInformer: opcInformer, - }, - }, - withInformer( - opcInformer, - pinnipedcontroller.NoOpFilter(), - controllerlib.InformerOption{}, - ), - ) -} - -// Sync implements controllerlib.Syncer. -func (c *dynamicConfigWatcherController) Sync(ctx controllerlib.Context) error { - // TODO Watch the configmap to find the issuer name, ingress url, etc. - // TODO Update some kind of in-memory representation of the configuration so the discovery endpoint can use it. - // TODO The discovery endpoint would return an error until all missing configuration options are - // filled in. - - opc, err := c.opcInformer. - Lister(). - OIDCProviderConfigs(ctx.Key.Namespace). - Get(ctx.Key.Name) - notFound := k8serrors.IsNotFound(err) - if err != nil && !notFound { - return fmt.Errorf("failed to get %s/%s oidcproviderconfig: %w", ctx.Key.Namespace, ctx.Key.Name, err) - } - - if notFound { - klog.InfoS( - "dynamicConfigWatcherController Sync found no oidcproviderconfig", - "oidcproviderconfig", - klog.KRef(ctx.Key.Namespace, ctx.Key.Name), - ) - if err := c.issuerSetter.SetIssuer(nil); err != nil { - klog.InfoS( - "dynamicConfigWatcherController Sync failed to set issuer", - "err", - err, - ) - } - return nil - } - - url, err := url.Parse(opc.Spec.Issuer) - if err != nil { - klog.InfoS( - "dynamicConfigWatcherController Sync failed to parse issuer", - "err", - err, - ) - return nil - } - - klog.InfoS( - "dynamicConfigWatcherController Sync issuer", - "oidcproviderconfig", - klog.KObj(opc), - "issuer", - url, - ) - if err := c.issuerSetter.SetIssuer(url); err != nil { - klog.InfoS( - "dynamicConfigWatcherController Sync failed to set issuer", - "err", - err, - ) - } - - return nil -} diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go new file mode 100644 index 000000000..204600aba --- /dev/null +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go @@ -0,0 +1,91 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package supervisorconfig + +import ( + "net/url" + + "k8s.io/apimachinery/pkg/labels" + "k8s.io/klog/v2" + + configinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions/config/v1alpha1" + pinnipedcontroller "go.pinniped.dev/internal/controller" + "go.pinniped.dev/internal/controllerlib" + "go.pinniped.dev/internal/oidc/provider" +) + +// ProvidersSetter can be notified of all known valid providers with its SetIssuer function. +// If there are no longer any valid issuers, then it can be called with no arguments. +// Implementations of this type should be thread-safe to support calls from multiple goroutines. +type ProvidersSetter interface { + SetProviders(oidcProviders ...*provider.OIDCProvider) +} + +type oidcProviderConfigWatcherController struct { + providerSetter ProvidersSetter + opcInformer configinformers.OIDCProviderConfigInformer +} + +// NewOIDCProviderConfigWatcherController creates a controllerlib.Controller that watches +// OIDCProviderConfig objects and notifies a callback object of the collection of provider configs. +func NewOIDCProviderConfigWatcherController( + issuerObserver ProvidersSetter, + opcInformer configinformers.OIDCProviderConfigInformer, + withInformer pinnipedcontroller.WithInformerOptionFunc, +) controllerlib.Controller { + return controllerlib.New( + controllerlib.Config{ + Name: "OIDCProviderConfigWatcherController", + Syncer: &oidcProviderConfigWatcherController{ + providerSetter: issuerObserver, + opcInformer: opcInformer, + }, + }, + withInformer( + opcInformer, + pinnipedcontroller.NoOpFilter(), + controllerlib.InformerOption{}, + ), + ) +} + +// Sync implements controllerlib.Syncer. +func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) error { + all, err := c.opcInformer.Lister().List(labels.Everything()) + if err != nil { + return err + } + + oidcProviders := make([]*provider.OIDCProvider, 0) + for _, opc := range all { + issuerURL, err := url.Parse(opc.Spec.Issuer) + if err != nil { + klog.InfoS( + "OIDCProviderConfigWatcherController Sync failed to parse issuer", + "err", + err, + ) + continue + } + oidcProvider := &provider.OIDCProvider{Issuer: issuerURL} + err = oidcProvider.Validate() + if err != nil { + klog.InfoS( + "OIDCProviderConfigWatcherController Sync could failed to validate OIDCProviderConfig", + "err", + err, + ) + continue + } + oidcProviders = append(oidcProviders, oidcProvider) + klog.InfoS( + "OIDCProviderConfigWatcherController Sync accepted OIDCProviderConfig", + "issuer", + issuerURL, + ) + } + + c.providerSetter.SetProviders(oidcProviders...) + return nil +} diff --git a/internal/oidc/discovery/discovery.go b/internal/oidc/discovery/discovery.go index 2293e9637..84ca1c77f 100644 --- a/internal/oidc/discovery/discovery.go +++ b/internal/oidc/discovery/discovery.go @@ -8,16 +8,13 @@ import ( "encoding/json" "fmt" "net/http" - "net/url" - - "go.pinniped.dev/internal/oidc" ) // Metadata holds all fields (that we care about) from the OpenID Provider Metadata section in the // OpenID Connect Discovery specification: // https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3. type Metadata struct { - // vvvRequiredvvv + // vvv Required vvv Issuer string `json:"issuer"` @@ -29,44 +26,28 @@ type Metadata struct { SubjectTypesSupported []string `json:"subject_types_supported"` IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"` - // ^^^Required^^^ + // ^^^ Required ^^^ - // vvvOptionalvvv + // vvv Optional vvv TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"` TokenEndpointAuthSigningAlgoValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"` ScopesSupported []string `json:"scopes_supported"` ClaimsSupported []string `json:"claims_supported"` - // ^^^Optional^^^ + // ^^^ Optional ^^^ } -// IssuerGetter holds onto an issuer which can be retrieved via its GetIssuer function. If there is -// no valid issuer, then nil will be returned. -// -// Implementations of this type should be thread-safe to support calls from multiple goroutines. -type IssuerGetter interface { - GetIssuer() *url.URL -} - -// New returns an http.Handler that will use information from the provided IssuerGetter to serve an -// OIDC discovery endpoint. -func New(ig IssuerGetter) http.Handler { +// New returns an http.Handler that serves an OIDC discovery endpoint. +func New(issuerURL string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - issuer := ig.GetIssuer() - if issuer == nil || r.URL.Path != issuer.Path+oidc.WellKnownURLPath { - http.Error(w, `{"error": "OIDC discovery not available (unknown issuer)"}`, http.StatusNotFound) - return - } - if r.Method != http.MethodGet { http.Error(w, `{"error": "Method not allowed (try GET)"}`, http.StatusMethodNotAllowed) return } - issuerURL := issuer.String() oidcConfig := Metadata{ Issuer: issuerURL, AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", issuerURL), diff --git a/internal/oidc/discovery/discovery_test.go b/internal/oidc/discovery/discovery_test.go index 49f46bebc..14f2d9b62 100644 --- a/internal/oidc/discovery/discovery_test.go +++ b/internal/oidc/discovery/discovery_test.go @@ -7,20 +7,18 @@ import ( "encoding/json" "net/http" "net/http/httptest" - "net/url" "testing" "github.com/stretchr/testify/require" "go.pinniped.dev/internal/oidc" - "go.pinniped.dev/internal/oidc/issuerprovider" ) func TestDiscovery(t *testing.T) { tests := []struct { name string - issuer *url.URL + issuer string method string path string @@ -29,37 +27,8 @@ func TestDiscovery(t *testing.T) { wantBody interface{} }{ { - name: "nil issuer", - method: http.MethodGet, - path: oidc.WellKnownURLPath, - wantStatus: http.StatusNotFound, - wantBody: map[string]string{ - "error": "OIDC discovery not available (unknown issuer)", - }, - }, - { - name: "root path mismatch", - issuer: must(url.Parse("https://some-issuer.com/some/path")), - method: http.MethodGet, - path: "/some/other/path" + oidc.WellKnownURLPath, - wantStatus: http.StatusNotFound, - wantBody: map[string]string{ - "error": "OIDC discovery not available (unknown issuer)", - }, - }, - { - name: "well-known path mismatch", - issuer: must(url.Parse("https://some-issuer.com/some/path")), - method: http.MethodGet, - path: "/some/path/that/is/not/the/well-known/path", - wantStatus: http.StatusNotFound, - wantBody: map[string]string{ - "error": "OIDC discovery not available (unknown issuer)", - }, - }, - { - name: "issuer path matches", - issuer: must(url.Parse("https://some-issuer.com/some/path")), + name: "happy path", + issuer: "https://some-issuer.com/some/path", method: http.MethodGet, path: "/some/path" + oidc.WellKnownURLPath, wantStatus: http.StatusOK, @@ -80,7 +49,7 @@ func TestDiscovery(t *testing.T) { }, { name: "bad method", - issuer: must(url.Parse("https://some-issuer.com")), + issuer: "https://some-issuer.com", method: http.MethodPost, path: oidc.WellKnownURLPath, wantStatus: http.StatusMethodNotAllowed, @@ -92,11 +61,7 @@ func TestDiscovery(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - p := issuerprovider.New() - err := p.SetIssuer(test.issuer) - require.NoError(t, err) - - handler := New(p) + handler := New(test.issuer) req := httptest.NewRequest(test.method, test.path, nil) rsp := httptest.NewRecorder() handler.ServeHTTP(rsp, req) @@ -115,10 +80,3 @@ func TestDiscovery(t *testing.T) { }) } } - -func must(u *url.URL, err error) *url.URL { - if err != nil { - panic(err) - } - return u -} diff --git a/internal/oidc/issuerprovider/issuerprovider.go b/internal/oidc/issuerprovider/issuerprovider.go deleted file mode 100644 index 36ec8c59d..000000000 --- a/internal/oidc/issuerprovider/issuerprovider.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package issuerprovider provides a thread-safe type that can hold on to an OIDC issuer name. -package issuerprovider - -import ( - "net/url" - "strings" - "sync" - - "go.pinniped.dev/internal/constable" -) - -// Provider is a type that can hold onto an issuer value, which may be nil. -// -// It is thread-safe. -type Provider struct { - mu sync.RWMutex - issuer *url.URL -} - -// New returns an empty Provider, i.e., one that holds a nil issuer. -func New() *Provider { - return &Provider{} -} - -// SetIssuer validates and sets the provided issuer. If validation fails, SetIssuer will return -// an error. -func (p *Provider) SetIssuer(issuer *url.URL) error { - if err := p.validateIssuer(issuer); err != nil { - return err - } - p.setIssuer(issuer) - return nil -} - -func (p *Provider) validateIssuer(issuer *url.URL) error { - if issuer == nil { - return nil - } - - if issuer.Scheme != "https" && removeMeAfterWeNoLongerNeedHTTPIssuerSupport(issuer.Scheme) { - return constable.Error(`issuer must have "https" scheme`) - } - - if issuer.User != nil { - return constable.Error(`issuer must not have username or password`) - } - - if strings.HasSuffix(issuer.Path, "/") { - return constable.Error(`issuer must not have trailing slash in path`) - } - - if issuer.RawQuery != "" { - return constable.Error(`issuer must not have query`) - } - - if issuer.Fragment != "" { - return constable.Error(`issuer must not have fragment`) - } - - return nil -} - -func (p *Provider) setIssuer(issuer *url.URL) { - p.mu.Lock() - defer p.mu.Unlock() - p.issuer = issuer -} - -func (p *Provider) GetIssuer() *url.URL { - p.mu.RLock() - defer p.mu.RUnlock() - return p.issuer -} - -func removeMeAfterWeNoLongerNeedHTTPIssuerSupport(scheme string) bool { - return scheme != "http" -} diff --git a/internal/oidc/provider/manager.go b/internal/oidc/provider/manager.go new file mode 100644 index 000000000..a50a70950 --- /dev/null +++ b/internal/oidc/provider/manager.go @@ -0,0 +1,115 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package provider + +import ( + "net/http" + "net/url" + "strings" + "sync" + + "k8s.io/klog/v2" + + "go.pinniped.dev/internal/oidc" + "go.pinniped.dev/internal/oidc/discovery" +) + +// Manager can manage multiple active OIDC providers. It acts as a request router for them. +// +// It is thread-safe. +type Manager struct { + mu sync.RWMutex + providerHandlers map[string]*providerHandler // map of issuer name to providerHandler + nextHandler http.Handler // the next handler in a chain, called when this manager didn't know how to handle a request +} + +// New returns an empty Manager. +// nextHandler will be invoked for any requests that could not be handled by this manager's providers. +func NewManager(nextHandler http.Handler) *Manager { + return &Manager{providerHandlers: make(map[string]*providerHandler), nextHandler: nextHandler} +} + +type providerHandler struct { + provider *OIDCProvider + discoveryHandler http.Handler +} + +func (h *providerHandler) Issuer() *url.URL { + return h.provider.Issuer +} + +// SetProviders adds or updates all the given providerHandlers using each provider's issuer string +// as the name of the provider to decide if it is an add or update operation. +// +// It also removes any providerHandlers that were previously added but were not passed in to +// the current invocation. +// +// This method assumes that all of the OIDCProvider arguments have already been validated +// by someone else before they are passed to this method. +func (c *Manager) SetProviders(oidcProviders ...*OIDCProvider) { + c.mu.Lock() + defer c.mu.Unlock() + // Add all of the incoming providers. + for _, incomingProvider := range oidcProviders { + issuerString := incomingProvider.Issuer.String() + c.providerHandlers[issuerString] = &providerHandler{ + provider: incomingProvider, + discoveryHandler: discovery.New(issuerString), + } + klog.InfoS("oidc provider manager added or updated issuer", "issuer", issuerString) + } + // Remove any providers that we previously handled but no longer exist. + for issuerKey := range c.providerHandlers { + if !findIssuerInListOfProviders(issuerKey, oidcProviders) { + delete(c.providerHandlers, issuerKey) + klog.InfoS("oidc provider manager removed issuer", "issuer", issuerKey) + } + } +} + +// ServeHTTP implements the http.Handler interface. +func (c *Manager) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + providerHandler := c.findProviderHandlerByIssuerURL(req.Host, req.URL.Path) + if providerHandler != nil { + if req.URL.Path == providerHandler.Issuer().Path+oidc.WellKnownURLPath { + providerHandler.discoveryHandler.ServeHTTP(resp, req) + return // handled! + } + klog.InfoS( + "oidc provider manager found issuer but could not handle request", + "method", req.Method, + "host", req.Host, + "path", req.URL.Path, + ) + } else { + klog.InfoS( + "oidc provider manager could not find issuer to handle request", + "method", req.Method, + "host", req.Host, + "path", req.URL.Path, + ) + } + // Didn't know how to handle this request, so send it along the chain for further processing. + c.nextHandler.ServeHTTP(resp, req) +} + +func (c *Manager) findProviderHandlerByIssuerURL(host, path string) *providerHandler { + for _, providerHandler := range c.providerHandlers { + pi := providerHandler.Issuer() + // TODO do we need to compare scheme? not sure how to get it from the http.Request object + if host == pi.Host && strings.HasPrefix(path, pi.Path) { // TODO probably need better logic here? also maybe needs some of the logic from inside ServeMux + return providerHandler + } + } + return nil +} + +func findIssuerInListOfProviders(issuer string, oidcProviders []*OIDCProvider) bool { + for _, provider := range oidcProviders { + if provider.Issuer.String() == issuer { + return true + } + } + return false +} diff --git a/internal/oidc/provider/oidcprovider.go b/internal/oidc/provider/oidcprovider.go new file mode 100644 index 000000000..c7c24de45 --- /dev/null +++ b/internal/oidc/provider/oidcprovider.go @@ -0,0 +1,50 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package provider + +import ( + "net/url" + "strings" + + "go.pinniped.dev/internal/constable" +) + +// OIDCProvider represents all of the settings and state for an OIDC provider. +type OIDCProvider struct { + Issuer *url.URL +} + +// Validate returns an error if there is anything wrong with the provider settings, or +// returns nil if there is nothing wrong with the settings. +func (p *OIDCProvider) Validate() error { + if p.Issuer == nil { + return constable.Error(`provider must have an issuer`) + } + + if p.Issuer.Scheme != "https" && p.removeMeAfterWeNoLongerNeedHTTPIssuerSupport(p.Issuer.Scheme) { + return constable.Error(`issuer must have "https" scheme`) + } + + if p.Issuer.User != nil { + return constable.Error(`issuer must not have username or password`) + } + + if strings.HasSuffix(p.Issuer.Path, "/") { + return constable.Error(`issuer must not have trailing slash in path`) + } + + if p.Issuer.RawQuery != "" { + return constable.Error(`issuer must not have query`) + } + + if p.Issuer.Fragment != "" { + return constable.Error(`issuer must not have fragment`) + } + + return nil +} + +func (p *OIDCProvider) removeMeAfterWeNoLongerNeedHTTPIssuerSupport(scheme string) bool { + return scheme != "http" +} diff --git a/internal/oidc/issuerprovider/issuerprovider_test.go b/internal/oidc/provider/oidcprovider_test.go similarity index 88% rename from internal/oidc/issuerprovider/issuerprovider_test.go rename to internal/oidc/provider/oidcprovider_test.go index 128502f43..2da0e3a83 100644 --- a/internal/oidc/issuerprovider/issuerprovider_test.go +++ b/internal/oidc/provider/oidcprovider_test.go @@ -1,7 +1,7 @@ // Copyright 2020 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package issuerprovider +package provider import ( "net/url" @@ -10,15 +10,16 @@ import ( "github.com/stretchr/testify/require" ) -func TestProvider(t *testing.T) { +func TestOIDCProviderValidations(t *testing.T) { tests := []struct { name string issuer *url.URL wantError string }{ { - name: "nil issuer", - issuer: nil, + name: "provider must have an issuer", + issuer: nil, + wantError: "provider must have an issuer", }, { name: "no scheme", @@ -67,14 +68,12 @@ func TestProvider(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - p := New() - err := p.SetIssuer(tt.issuer) + p := OIDCProvider{Issuer: tt.issuer} + err := p.Validate() if tt.wantError != "" { require.EqualError(t, err, tt.wantError) - require.Nil(t, p.GetIssuer()) } else { require.NoError(t, err) - require.Equal(t, tt.issuer, p.GetIssuer()) } }) } diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index d2e0bca29..148e1878d 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -13,9 +13,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned" "go.pinniped.dev/internal/here" "go.pinniped.dev/test/library" ) @@ -24,7 +26,6 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { env := library.IntegrationEnv(t) client := library.NewPinnipedClientset(t) - httpClient := &http.Client{} ns := env.SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() @@ -32,7 +33,6 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { // Temporarily remove any existing OIDCProviderConfigs from the cluster so we can test from a clean slate. originalConfigList, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).List(ctx, metav1.ListOptions{}) require.NoError(t, err) - for _, config := range originalConfigList.Items { err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(ctx, config.Name, metav1.DeleteOptions{}) require.NoError(t, err) @@ -52,51 +52,85 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { }) // Test that there is no default discovery endpoint available when there are no OIDCProviderConfigs. + requireDiscoveryEndpointIsNotFound(t, fmt.Sprintf("http://%s", env.SupervisorAddress)) + + // Define several unique issuer strings. + issuer1 := fmt.Sprintf("http://%s/nested/issuer1", env.SupervisorAddress) + issuer2 := fmt.Sprintf("http://%s/nested/issuer2", env.SupervisorAddress) + issuer3 := fmt.Sprintf("http://%s/issuer3", env.SupervisorAddress) + issuer4 := fmt.Sprintf("http://%s/issuer4", env.SupervisorAddress) + + // When OIDCProviderConfig are created in sequence they each cause a discovery endpoint to appear only for as long as the OIDCProviderConfig exists. + createdOIDCProviderConfig1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer1, "from-integration-test1") + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig1, client, ns, issuer1) + createdOIDCProviderConfig2 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer2, "from-integration-test2") + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig2, client, ns, issuer2) + + // When multiple OIDCProviderConfigs exist at the same time they each serve a unique discovery endpoint. + createdOIDCProviderConfig3 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer3, "from-integration-test3") + createdOIDCProviderConfig4 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer4, "from-integration-test4") + requireWellKnownEndpointIsWorking(t, issuer3) // discovery for issuer3 is still working after issuer4 started working + + // When they are deleted they stop serving discovery endpoints. + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig3, client, ns, issuer2) + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig4, client, ns, issuer2) +} + +func requireDiscoveryEndpointIsNotFound(t *testing.T, issuerName string) { + t.Helper() + httpClient := &http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + requestNonExistentPath, err := http.NewRequestWithContext( ctx, http.MethodGet, - fmt.Sprintf("http://%s/.well-known/openid-configuration", env.SupervisorAddress), + fmt.Sprintf("%s/.well-known/openid-configuration", issuerName), nil, ) + + var response *http.Response + assert.Eventually(t, func() bool { + response, err = httpClient.Do(requestNonExistentPath) //nolint:bodyclose + return err == nil && response.StatusCode == http.StatusNotFound + }, 10*time.Second, 200*time.Millisecond) require.NoError(t, err) - notFoundResponse, err := httpClient.Do(requestNonExistentPath) + require.Equal(t, http.StatusNotFound, response.StatusCode) + err = response.Body.Close() require.NoError(t, err) - require.Equal(t, 404, notFoundResponse.StatusCode) - err = notFoundResponse.Body.Close() +} + +func requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t *testing.T, client pinnipedclientset.Interface, ns string, issuerName string, oidcProviderConfigName string) *v1alpha1.OIDCProviderConfig { + t.Helper() + newOIDCProviderConfig := createOIDCProviderConfig(t, oidcProviderConfigName, client, ns, issuerName) + requireWellKnownEndpointIsWorking(t, issuerName) + return newOIDCProviderConfig +} + +func requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t *testing.T, existingOIDCProviderConfig *v1alpha1.OIDCProviderConfig, client pinnipedclientset.Interface, ns string, issuerName string) { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + // Delete the OIDCProviderConfig. + err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(ctx, existingOIDCProviderConfig.Name, metav1.DeleteOptions{}) require.NoError(t, err) - // Create a new OIDCProviderConfig with a known issuer. - issuer := fmt.Sprintf("http://%s/nested/issuer", env.SupervisorAddress) - newOIDCProviderConfig := v1alpha1.OIDCProviderConfig{ - TypeMeta: metav1.TypeMeta{ - Kind: "OIDCProviderConfig", - APIVersion: v1alpha1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nested-issuser-config-from-integration-test", - Namespace: ns, - }, - Spec: v1alpha1.OIDCProviderConfigSpec{ - Issuer: issuer, - }, - } - _, err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(ctx, &newOIDCProviderConfig, metav1.CreateOptions{}) - require.NoError(t, err) + // Fetch that same discovery endpoint as before, but now it should not exist anymore. Give it some time for the endpoint to go away. + requireDiscoveryEndpointIsNotFound(t, issuerName) +} - // When this test has finished, clean up the new OIDCProviderConfig. - t.Cleanup(func() { - cleanupCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() +func requireWellKnownEndpointIsWorking(t *testing.T, issuerName string) { + t.Helper() + httpClient := &http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() - err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(cleanupCtx, newOIDCProviderConfig.Name, metav1.DeleteOptions{}) - require.NoError(t, err) - }) - - // Define a request to the new discovery endpoint which should have been created for the above OIDCProviderConfig. + // Define a request to the new discovery endpoint which should have been created by an OIDCProviderConfig. requestDiscoveryEndpoint, err := http.NewRequestWithContext( ctx, http.MethodGet, - fmt.Sprintf("http://%s/nested/issuer/.well-known/openid-configuration", env.SupervisorAddress), + fmt.Sprintf("%s/.well-known/openid-configuration", issuerName), nil, ) require.NoError(t, err) @@ -104,7 +138,7 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { // Fetch that discovery endpoint. Give it some time for the endpoint to come into existence. var response *http.Response assert.Eventually(t, func() bool { - response, err = httpClient.Do(requestDiscoveryEndpoint) //nolint:bodyclose // the body is closed below after it is read + response, err = httpClient.Do(requestDiscoveryEndpoint) //nolint:bodyclose return err == nil && response.StatusCode == http.StatusOK }, 10*time.Second, 200*time.Millisecond) require.NoError(t, err) @@ -129,8 +163,45 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["RS256"] }`) - expectedJSON := fmt.Sprintf(expectedResultTemplate, issuer, issuer, issuer, issuer) + expectedJSON := fmt.Sprintf(expectedResultTemplate, issuerName, issuerName, issuerName, issuerName) require.Equal(t, "application/json", response.Header.Get("content-type")) require.JSONEq(t, expectedJSON, string(responseBody)) } + +func createOIDCProviderConfig(t *testing.T, oidcProviderConfigName string, client pinnipedclientset.Interface, ns string, issuerName string) *v1alpha1.OIDCProviderConfig { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + newOIDCProviderConfig := v1alpha1.OIDCProviderConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "OIDCProviderConfig", + APIVersion: v1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: oidcProviderConfigName, + Namespace: ns, + }, + Spec: v1alpha1.OIDCProviderConfigSpec{ + Issuer: issuerName, + }, + } + createdOIDCProviderConfig, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(ctx, &newOIDCProviderConfig, metav1.CreateOptions{}) + require.NoError(t, err) + + // When this test has finished, be sure to clean up the new OIDCProviderConfig. + t.Cleanup(func() { + cleanupCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(cleanupCtx, newOIDCProviderConfig.Name, metav1.DeleteOptions{}) + notFound := k8serrors.IsNotFound(err) + // It's okay if it is not found, because it might have been deleted by another part of this test. + if !notFound { + require.NoError(t, err) + } + }) + + return createdOIDCProviderConfig +} From da00fc708f75f7607d1a1f8adc97ca33f662c4ca Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Thu, 8 Oct 2020 13:27:45 -0400 Subject: [PATCH 15/28] supervisor-oidc: checkpoint: add status to provider CRD Signed-off-by: Ryan Richard --- .../v1alpha1/types_oidcproviderconfig.go.tmpl | 26 ++- cmd/pinniped-supervisor/main.go | 6 +- deploy-supervisor/rbac.yaml | 2 +- ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 19 ++- generated/1.17/README.adoc | 21 ++- .../v1alpha1/types_oidcproviderconfig.go | 26 ++- .../config/v1alpha1/zz_generated.deepcopy.go | 17 ++ .../v1alpha1/fake/fake_oidcproviderconfig.go | 12 ++ .../config/v1alpha1/oidcproviderconfig.go | 17 ++ .../client/openapi/zz_generated.openapi.go | 40 ++++- ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 19 ++- generated/1.18/README.adoc | 21 ++- .../v1alpha1/types_oidcproviderconfig.go | 26 ++- .../config/v1alpha1/zz_generated.deepcopy.go | 17 ++ .../v1alpha1/fake/fake_oidcproviderconfig.go | 12 ++ .../config/v1alpha1/oidcproviderconfig.go | 17 ++ .../client/openapi/zz_generated.openapi.go | 40 ++++- ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 19 ++- generated/1.19/README.adoc | 21 ++- .../v1alpha1/types_oidcproviderconfig.go | 26 ++- .../config/v1alpha1/zz_generated.deepcopy.go | 17 ++ .../v1alpha1/fake/fake_oidcproviderconfig.go | 12 ++ .../config/v1alpha1/oidcproviderconfig.go | 17 ++ .../client/openapi/zz_generated.openapi.go | 40 ++++- ...nfig.pinniped.dev_oidcproviderconfigs.yaml | 19 ++- .../oidcproviderconfig_watcher.go | 150 +++++++++++++++--- test/integration/supervisor_discovery_test.go | 63 +++++++- 27 files changed, 669 insertions(+), 53 deletions(-) diff --git a/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl index 7000aead5..a2850bdf8 100644 --- a/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl +++ b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl @@ -5,6 +5,15 @@ package v1alpha1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// +kubebuilder:validation:Enum=Success;Duplicate;Invalid +type OIDCProviderStatus string + +const ( + SuccessOIDCProviderStatus = OIDCProviderStatus("Success") + DuplicateOIDCProviderStatus = OIDCProviderStatus("Duplicate") + InvalidOIDCProviderStatus = OIDCProviderStatus("Invalid") +) + // OIDCProviderConfigSpec is a struct that describes an OIDC Provider. type OIDCProviderConfigSpec struct { // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the @@ -19,6 +28,18 @@ type OIDCProviderConfigSpec struct { Issuer string `json:"issuer"` } +// OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. +type OIDCProviderConfigStatus struct { + // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can + // represent success or failure. + // +optional + Status OIDCProviderStatus `json:"status,omitempty"` + + // Message provides human-readable details about the Status. + // +optional + Message string `json:"message,omitempty"` +} + // OIDCProviderConfig describes the configuration of an OIDC provider. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -28,7 +49,10 @@ type OIDCProviderConfig struct { metav1.ObjectMeta `json:"metadata,omitempty"` // Spec of the OIDC provider. - Spec OIDCProviderConfigSpec `json:"status"` + Spec OIDCProviderConfigSpec `json:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` } // List of OIDCProviderConfig objects. diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index 9b111f50a..dbbf5cfcf 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -12,6 +12,7 @@ import ( "os/signal" "time" + "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/pkg/version" "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest" @@ -61,6 +62,7 @@ func waitForSignal() os.Signal { func startControllers( ctx context.Context, issuerProvider *provider.Manager, + pinnipedClient pinnipedclientset.Interface, pinnipedInformers pinnipedinformers.SharedInformerFactory, ) { // Create controller manager. @@ -69,6 +71,8 @@ func startControllers( WithController( supervisorconfig.NewOIDCProviderConfigWatcherController( issuerProvider, + clock.RealClock{}, + pinnipedClient, pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(), controllerlib.WithInformer, ), @@ -111,7 +115,7 @@ func run(serverInstallationNamespace string) error { ) oidProvidersManager := provider.NewManager(http.NotFoundHandler()) - startControllers(ctx, oidProvidersManager, pinnipedInformers) + startControllers(ctx, oidProvidersManager, pinnipedClient, pinnipedInformers) //nolint: gosec // Intentionally binding to all network interfaces. l, err := net.Listen("tcp", ":80") diff --git a/deploy-supervisor/rbac.yaml b/deploy-supervisor/rbac.yaml index a4b34e9a6..bfa4ac951 100644 --- a/deploy-supervisor/rbac.yaml +++ b/deploy-supervisor/rbac.yaml @@ -15,7 +15,7 @@ metadata: rules: - apiGroups: [config.pinniped.dev] resources: [oidcproviderconfigs] - verbs: [get, list, watch] + verbs: [update, get, list, watch] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 diff --git a/deploy/config.pinniped.dev_oidcproviderconfigs.yaml b/deploy/config.pinniped.dev_oidcproviderconfigs.yaml index 961da2538..c1cb55256 100644 --- a/deploy/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/deploy/config.pinniped.dev_oidcproviderconfigs.yaml @@ -35,7 +35,7 @@ spec: type: string metadata: type: object - status: + spec: description: Spec of the OIDC provider. properties: issuer: @@ -52,8 +52,23 @@ spec: required: - issuer type: object + status: + description: Status of the OIDC provider. + properties: + message: + description: Message provides human-readable details about the Status. + type: string + status: + description: Status holds an enum that describes the state of this + OIDCProvider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object required: - - status + - spec type: object served: true storage: true diff --git a/generated/1.17/README.adoc b/generated/1.17/README.adoc index 1953af028..716d1b036 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -110,7 +110,8 @@ OIDCProviderConfig describes the configuration of an OIDC provider. | Field | Description | *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfigstatus[$$OIDCProviderConfigStatus$$]__ | Status of the OIDC provider. |=== @@ -134,6 +135,24 @@ OIDCProviderConfigSpec is a struct that describes an OIDC Provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfigstatus"] +==== OIDCProviderConfigStatus + +OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-config-v1alpha1-oidcproviderconfig[$$OIDCProviderConfig$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure. +| *`message`* __string__ | Message provides human-readable details about the Status. +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 diff --git a/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go index 7000aead5..75931e70b 100644 --- a/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go +++ b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -5,6 +5,15 @@ package v1alpha1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// +kubebuilder:validation:Enum=Success;Duplicate;Invalid +type OIDCProviderStatus string + +const ( + SuccessOIDCProviderStatus = OIDCProviderStatus("Success") + DuplicateOIDCProviderStatus = OIDCProviderStatus("Duplicate") + InvalidOIDCProviderStatus = OIDCProviderStatus("Invalid") +) + // OIDCProviderConfigSpec is a struct that describes an OIDC Provider. type OIDCProviderConfigSpec struct { // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the @@ -19,6 +28,18 @@ type OIDCProviderConfigSpec struct { Issuer string `json:"issuer"` } +// OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. +type OIDCProviderConfigStatus struct { + // Status holds an enum that describes the state of this OIDCProvider. Note that this Status can + // represent success or failure. + // +optional + Status OIDCProviderStatus `json:"status,omitempty"` + + // Message provides human-readable details about the Status. + // +optional + Message string `json:"message,omitempty"` +} + // OIDCProviderConfig describes the configuration of an OIDC provider. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -28,7 +49,10 @@ type OIDCProviderConfig struct { metav1.ObjectMeta `json:"metadata,omitempty"` // Spec of the OIDC provider. - Spec OIDCProviderConfigSpec `json:"status"` + Spec OIDCProviderConfigSpec `json:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` } // List of OIDCProviderConfig objects. diff --git a/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go index f45d80c35..253a5c5ba 100644 --- a/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -138,6 +138,7 @@ func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec + out.Status = in.Status return } @@ -207,3 +208,19 @@ func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProviderConfigStatus) DeepCopyInto(out *OIDCProviderConfigStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigStatus. +func (in *OIDCProviderConfigStatus) DeepCopy() *OIDCProviderConfigStatus { + if in == nil { + return nil + } + out := new(OIDCProviderConfigStatus) + in.DeepCopyInto(out) + return out +} diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go index 6cd7dc42b..a0a853940 100644 --- a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -87,6 +87,18 @@ func (c *FakeOIDCProviderConfigs) Update(oIDCProviderConfig *v1alpha1.OIDCProvid return obj.(*v1alpha1.OIDCProviderConfig), err } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeOIDCProviderConfigs) UpdateStatus(oIDCProviderConfig *v1alpha1.OIDCProviderConfig) (*v1alpha1.OIDCProviderConfig, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(oidcproviderconfigsResource, "status", c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + // Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. func (c *FakeOIDCProviderConfigs) Delete(name string, options *v1.DeleteOptions) error { _, err := c.Fake. diff --git a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go index 23f8760fd..b080830dd 100644 --- a/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -26,6 +26,7 @@ type OIDCProviderConfigsGetter interface { type OIDCProviderConfigInterface interface { Create(*v1alpha1.OIDCProviderConfig) (*v1alpha1.OIDCProviderConfig, error) Update(*v1alpha1.OIDCProviderConfig) (*v1alpha1.OIDCProviderConfig, error) + UpdateStatus(*v1alpha1.OIDCProviderConfig) (*v1alpha1.OIDCProviderConfig, error) Delete(name string, options *v1.DeleteOptions) error DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error Get(name string, options v1.GetOptions) (*v1alpha1.OIDCProviderConfig, error) @@ -119,6 +120,22 @@ func (c *oIDCProviderConfigs) Update(oIDCProviderConfig *v1alpha1.OIDCProviderCo return } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *oIDCProviderConfigs) UpdateStatus(oIDCProviderConfig *v1alpha1.OIDCProviderConfig) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(oIDCProviderConfig.Name). + SubResource("status"). + Body(oIDCProviderConfig). + Do(). + Into(result) + return +} + // Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. func (c *oIDCProviderConfigs) Delete(name string, options *v1.DeleteOptions) error { return c.client.Delete(). diff --git a/generated/1.17/client/openapi/zz_generated.openapi.go b/generated/1.17/client/openapi/zz_generated.openapi.go index 55aecb0a4..d25d0c88e 100644 --- a/generated/1.17/client/openapi/zz_generated.openapi.go +++ b/generated/1.17/client/openapi/zz_generated.openapi.go @@ -25,6 +25,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfig": schema_117_apis_config_v1alpha1_OIDCProviderConfig(ref), "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigList": schema_117_apis_config_v1alpha1_OIDCProviderConfigList(ref), "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec": schema_117_apis_config_v1alpha1_OIDCProviderConfigSpec(ref), + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigStatus": schema_117_apis_config_v1alpha1_OIDCProviderConfigStatus(ref), "go.pinniped.dev/generated/1.17/apis/idp/v1alpha1.Condition": schema_117_apis_idp_v1alpha1_Condition(ref), "go.pinniped.dev/generated/1.17/apis/idp/v1alpha1.TLSSpec": schema_117_apis_idp_v1alpha1_TLSSpec(ref), "go.pinniped.dev/generated/1.17/apis/idp/v1alpha1.WebhookIdentityProvider": schema_117_apis_idp_v1alpha1_WebhookIdentityProvider(ref), @@ -315,18 +316,24 @@ func schema_117_apis_config_v1alpha1_OIDCProviderConfig(ref common.ReferenceCall Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, - "status": { + "spec": { SchemaProps: spec.SchemaProps{ Description: "Spec of the OIDC provider.", Ref: ref("go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec"), }, }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the OIDC provider.", + Ref: ref("go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigStatus"), + }, + }, }, - Required: []string{"status"}, + Required: []string{"spec"}, }, }, Dependencies: []string{ - "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigSpec", "go.pinniped.dev/generated/1.17/apis/config/v1alpha1.OIDCProviderConfigStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -397,6 +404,33 @@ func schema_117_apis_config_v1alpha1_OIDCProviderConfigSpec(ref common.Reference } } +func schema_117_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message provides human-readable details about the Status.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_117_apis_idp_v1alpha1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml index 961da2538..c1cb55256 100644 --- a/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -35,7 +35,7 @@ spec: type: string metadata: type: object - status: + spec: description: Spec of the OIDC provider. properties: issuer: @@ -52,8 +52,23 @@ spec: required: - issuer type: object + status: + description: Status of the OIDC provider. + properties: + message: + description: Message provides human-readable details about the Status. + type: string + status: + description: Status holds an enum that describes the state of this + OIDCProvider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object required: - - status + - spec type: object served: true storage: true diff --git a/generated/1.18/README.adoc b/generated/1.18/README.adoc index 829768018..6eef02467 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -110,7 +110,8 @@ OIDCProviderConfig describes the configuration of an OIDC provider. | Field | Description | *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfigstatus[$$OIDCProviderConfigStatus$$]__ | Status of the OIDC provider. |=== @@ -134,6 +135,24 @@ OIDCProviderConfigSpec is a struct that describes an OIDC Provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfigstatus"] +==== OIDCProviderConfigStatus + +OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-config-v1alpha1-oidcproviderconfig[$$OIDCProviderConfig$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure. +| *`message`* __string__ | Message provides human-readable details about the Status. +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 diff --git a/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go index 7000aead5..75931e70b 100644 --- a/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go +++ b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -5,6 +5,15 @@ package v1alpha1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// +kubebuilder:validation:Enum=Success;Duplicate;Invalid +type OIDCProviderStatus string + +const ( + SuccessOIDCProviderStatus = OIDCProviderStatus("Success") + DuplicateOIDCProviderStatus = OIDCProviderStatus("Duplicate") + InvalidOIDCProviderStatus = OIDCProviderStatus("Invalid") +) + // OIDCProviderConfigSpec is a struct that describes an OIDC Provider. type OIDCProviderConfigSpec struct { // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the @@ -19,6 +28,18 @@ type OIDCProviderConfigSpec struct { Issuer string `json:"issuer"` } +// OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. +type OIDCProviderConfigStatus struct { + // Status holds an enum that describes the state of this OIDCProvider. Note that this Status can + // represent success or failure. + // +optional + Status OIDCProviderStatus `json:"status,omitempty"` + + // Message provides human-readable details about the Status. + // +optional + Message string `json:"message,omitempty"` +} + // OIDCProviderConfig describes the configuration of an OIDC provider. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -28,7 +49,10 @@ type OIDCProviderConfig struct { metav1.ObjectMeta `json:"metadata,omitempty"` // Spec of the OIDC provider. - Spec OIDCProviderConfigSpec `json:"status"` + Spec OIDCProviderConfigSpec `json:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` } // List of OIDCProviderConfig objects. diff --git a/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go index f45d80c35..253a5c5ba 100644 --- a/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -138,6 +138,7 @@ func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec + out.Status = in.Status return } @@ -207,3 +208,19 @@ func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProviderConfigStatus) DeepCopyInto(out *OIDCProviderConfigStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigStatus. +func (in *OIDCProviderConfigStatus) DeepCopy() *OIDCProviderConfigStatus { + if in == nil { + return nil + } + out := new(OIDCProviderConfigStatus) + in.DeepCopyInto(out) + return out +} diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go index fc4305808..a2c2db1b8 100644 --- a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -89,6 +89,18 @@ func (c *FakeOIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig return obj.(*v1alpha1.OIDCProviderConfig), err } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeOIDCProviderConfigs) UpdateStatus(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(oidcproviderconfigsResource, "status", c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + // Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. func (c *FakeOIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. diff --git a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go index 99042aa34..c1abf9ed6 100644 --- a/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -27,6 +27,7 @@ type OIDCProviderConfigsGetter interface { type OIDCProviderConfigInterface interface { Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (*v1alpha1.OIDCProviderConfig, error) Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) + UpdateStatus(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.OIDCProviderConfig, error) @@ -122,6 +123,22 @@ func (c *oIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig *v1 return } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *oIDCProviderConfigs) UpdateStatus(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(oIDCProviderConfig.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(oIDCProviderConfig). + Do(ctx). + Into(result) + return +} + // Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. func (c *oIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { return c.client.Delete(). diff --git a/generated/1.18/client/openapi/zz_generated.openapi.go b/generated/1.18/client/openapi/zz_generated.openapi.go index 76c671b6e..eb0e2dfa0 100644 --- a/generated/1.18/client/openapi/zz_generated.openapi.go +++ b/generated/1.18/client/openapi/zz_generated.openapi.go @@ -25,6 +25,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfig": schema_118_apis_config_v1alpha1_OIDCProviderConfig(ref), "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigList": schema_118_apis_config_v1alpha1_OIDCProviderConfigList(ref), "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec": schema_118_apis_config_v1alpha1_OIDCProviderConfigSpec(ref), + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigStatus": schema_118_apis_config_v1alpha1_OIDCProviderConfigStatus(ref), "go.pinniped.dev/generated/1.18/apis/idp/v1alpha1.Condition": schema_118_apis_idp_v1alpha1_Condition(ref), "go.pinniped.dev/generated/1.18/apis/idp/v1alpha1.TLSSpec": schema_118_apis_idp_v1alpha1_TLSSpec(ref), "go.pinniped.dev/generated/1.18/apis/idp/v1alpha1.WebhookIdentityProvider": schema_118_apis_idp_v1alpha1_WebhookIdentityProvider(ref), @@ -315,18 +316,24 @@ func schema_118_apis_config_v1alpha1_OIDCProviderConfig(ref common.ReferenceCall Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, - "status": { + "spec": { SchemaProps: spec.SchemaProps{ Description: "Spec of the OIDC provider.", Ref: ref("go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec"), }, }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the OIDC provider.", + Ref: ref("go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigStatus"), + }, + }, }, - Required: []string{"status"}, + Required: []string{"spec"}, }, }, Dependencies: []string{ - "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigSpec", "go.pinniped.dev/generated/1.18/apis/config/v1alpha1.OIDCProviderConfigStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -397,6 +404,33 @@ func schema_118_apis_config_v1alpha1_OIDCProviderConfigSpec(ref common.Reference } } +func schema_118_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message provides human-readable details about the Status.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_118_apis_idp_v1alpha1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml index 961da2538..c1cb55256 100644 --- a/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -35,7 +35,7 @@ spec: type: string metadata: type: object - status: + spec: description: Spec of the OIDC provider. properties: issuer: @@ -52,8 +52,23 @@ spec: required: - issuer type: object + status: + description: Status of the OIDC provider. + properties: + message: + description: Message provides human-readable details about the Status. + type: string + status: + description: Status holds an enum that describes the state of this + OIDCProvider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object required: - - status + - spec type: object served: true storage: true diff --git a/generated/1.19/README.adoc b/generated/1.19/README.adoc index 6de470167..3008fb692 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -110,7 +110,8 @@ OIDCProviderConfig describes the configuration of an OIDC provider. | Field | Description | *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfigspec[$$OIDCProviderConfigSpec$$]__ | Spec of the OIDC provider. +| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfigstatus[$$OIDCProviderConfigStatus$$]__ | Status of the OIDC provider. |=== @@ -134,6 +135,24 @@ OIDCProviderConfigSpec is a struct that describes an OIDC Provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfigstatus"] +==== OIDCProviderConfigStatus + +OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-config-v1alpha1-oidcproviderconfig[$$OIDCProviderConfig$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure. +| *`message`* __string__ | Message provides human-readable details about the Status. +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 diff --git a/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go index 7000aead5..75931e70b 100644 --- a/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go +++ b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -5,6 +5,15 @@ package v1alpha1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// +kubebuilder:validation:Enum=Success;Duplicate;Invalid +type OIDCProviderStatus string + +const ( + SuccessOIDCProviderStatus = OIDCProviderStatus("Success") + DuplicateOIDCProviderStatus = OIDCProviderStatus("Duplicate") + InvalidOIDCProviderStatus = OIDCProviderStatus("Invalid") +) + // OIDCProviderConfigSpec is a struct that describes an OIDC Provider. type OIDCProviderConfigSpec struct { // Issuer is the OIDC Provider's issuer, per the OIDC Discovery Metadata document, as well as the @@ -19,6 +28,18 @@ type OIDCProviderConfigSpec struct { Issuer string `json:"issuer"` } +// OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. +type OIDCProviderConfigStatus struct { + // Status holds an enum that describes the state of this OIDCProvider. Note that this Status can + // represent success or failure. + // +optional + Status OIDCProviderStatus `json:"status,omitempty"` + + // Message provides human-readable details about the Status. + // +optional + Message string `json:"message,omitempty"` +} + // OIDCProviderConfig describes the configuration of an OIDC provider. // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -28,7 +49,10 @@ type OIDCProviderConfig struct { metav1.ObjectMeta `json:"metadata,omitempty"` // Spec of the OIDC provider. - Spec OIDCProviderConfigSpec `json:"status"` + Spec OIDCProviderConfigSpec `json:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` } // List of OIDCProviderConfig objects. diff --git a/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go index f45d80c35..253a5c5ba 100644 --- a/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -138,6 +138,7 @@ func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec + out.Status = in.Status return } @@ -207,3 +208,19 @@ func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProviderConfigStatus) DeepCopyInto(out *OIDCProviderConfigStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProviderConfigStatus. +func (in *OIDCProviderConfigStatus) DeepCopy() *OIDCProviderConfigStatus { + if in == nil { + return nil + } + out := new(OIDCProviderConfigStatus) + in.DeepCopyInto(out) + return out +} diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go index 8e01a0672..5011336e7 100644 --- a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -89,6 +89,18 @@ func (c *FakeOIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig return obj.(*v1alpha1.OIDCProviderConfig), err } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeOIDCProviderConfigs) UpdateStatus(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(oidcproviderconfigsResource, "status", c.ns, oIDCProviderConfig), &v1alpha1.OIDCProviderConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.OIDCProviderConfig), err +} + // Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. func (c *FakeOIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. diff --git a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go index 7b32bf1e6..42765a4cb 100644 --- a/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -27,6 +27,7 @@ type OIDCProviderConfigsGetter interface { type OIDCProviderConfigInterface interface { Create(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.CreateOptions) (*v1alpha1.OIDCProviderConfig, error) Update(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) + UpdateStatus(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (*v1alpha1.OIDCProviderConfig, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.OIDCProviderConfig, error) @@ -122,6 +123,22 @@ func (c *oIDCProviderConfigs) Update(ctx context.Context, oIDCProviderConfig *v1 return } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *oIDCProviderConfigs) UpdateStatus(ctx context.Context, oIDCProviderConfig *v1alpha1.OIDCProviderConfig, opts v1.UpdateOptions) (result *v1alpha1.OIDCProviderConfig, err error) { + result = &v1alpha1.OIDCProviderConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("oidcproviderconfigs"). + Name(oIDCProviderConfig.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(oIDCProviderConfig). + Do(ctx). + Into(result) + return +} + // Delete takes name of the oIDCProviderConfig and deletes it. Returns an error if one occurs. func (c *oIDCProviderConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { return c.client.Delete(). diff --git a/generated/1.19/client/openapi/zz_generated.openapi.go b/generated/1.19/client/openapi/zz_generated.openapi.go index 95454e7be..c7ed50845 100644 --- a/generated/1.19/client/openapi/zz_generated.openapi.go +++ b/generated/1.19/client/openapi/zz_generated.openapi.go @@ -25,6 +25,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfig": schema_119_apis_config_v1alpha1_OIDCProviderConfig(ref), "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigList": schema_119_apis_config_v1alpha1_OIDCProviderConfigList(ref), "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec": schema_119_apis_config_v1alpha1_OIDCProviderConfigSpec(ref), + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigStatus": schema_119_apis_config_v1alpha1_OIDCProviderConfigStatus(ref), "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1.Condition": schema_119_apis_idp_v1alpha1_Condition(ref), "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1.TLSSpec": schema_119_apis_idp_v1alpha1_TLSSpec(ref), "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1.WebhookIdentityProvider": schema_119_apis_idp_v1alpha1_WebhookIdentityProvider(ref), @@ -316,18 +317,24 @@ func schema_119_apis_config_v1alpha1_OIDCProviderConfig(ref common.ReferenceCall Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), }, }, - "status": { + "spec": { SchemaProps: spec.SchemaProps{ Description: "Spec of the OIDC provider.", Ref: ref("go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec"), }, }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the OIDC provider.", + Ref: ref("go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigStatus"), + }, + }, }, - Required: []string{"status"}, + Required: []string{"spec"}, }, }, Dependencies: []string{ - "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigSpec", "go.pinniped.dev/generated/1.19/apis/config/v1alpha1.OIDCProviderConfigStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -398,6 +405,33 @@ func schema_119_apis_config_v1alpha1_OIDCProviderConfigSpec(ref common.Reference } } +func schema_119_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message provides human-readable details about the Status.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_119_apis_idp_v1alpha1_Condition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml index 961da2538..c1cb55256 100644 --- a/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -35,7 +35,7 @@ spec: type: string metadata: type: object - status: + spec: description: Spec of the OIDC provider. properties: issuer: @@ -52,8 +52,23 @@ spec: required: - issuer type: object + status: + description: Status of the OIDC provider. + properties: + message: + description: Message provides human-readable details about the Status. + type: string + status: + description: Status holds an enum that describes the state of this + OIDCProvider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object required: - - status + - spec type: object served: true storage: true diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go index 204600aba..cd827c949 100644 --- a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go @@ -4,11 +4,19 @@ package supervisorconfig import ( + "context" + "fmt" "net/url" + "strings" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/clock" + "k8s.io/client-go/util/retry" "k8s.io/klog/v2" + configv1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned" configinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions/config/v1alpha1" pinnipedcontroller "go.pinniped.dev/internal/controller" "go.pinniped.dev/internal/controllerlib" @@ -24,13 +32,17 @@ type ProvidersSetter interface { type oidcProviderConfigWatcherController struct { providerSetter ProvidersSetter + clock clock.Clock + client pinnipedclientset.Interface opcInformer configinformers.OIDCProviderConfigInformer } // NewOIDCProviderConfigWatcherController creates a controllerlib.Controller that watches // OIDCProviderConfig objects and notifies a callback object of the collection of provider configs. func NewOIDCProviderConfigWatcherController( - issuerObserver ProvidersSetter, + providerSetter ProvidersSetter, + clock clock.Clock, + client pinnipedclientset.Interface, opcInformer configinformers.OIDCProviderConfigInformer, withInformer pinnipedcontroller.WithInformerOptionFunc, ) controllerlib.Controller { @@ -38,7 +50,9 @@ func NewOIDCProviderConfigWatcherController( controllerlib.Config{ Name: "OIDCProviderConfigWatcherController", Syncer: &oidcProviderConfigWatcherController{ - providerSetter: issuerObserver, + providerSetter: providerSetter, + clock: clock, + client: client, opcInformer: opcInformer, }, }, @@ -57,35 +71,133 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er return err } + issuerCounts := make(map[string]int) + for _, opc := range all { + issuerCounts[opc.Spec.Issuer] = issuerCounts[opc.Spec.Issuer] + 1 + } + + errs := newMultiError() + oidcProviders := make([]*provider.OIDCProvider, 0) for _, opc := range all { - issuerURL, err := url.Parse(opc.Spec.Issuer) - if err != nil { - klog.InfoS( - "OIDCProviderConfigWatcherController Sync failed to parse issuer", - "err", - err, - ) + if issuerCount := issuerCounts[opc.Spec.Issuer]; issuerCount > 1 { + if err := c.updateStatus( + ctx.Context, + opc.Namespace, + opc.Name, + configv1alpha1.DuplicateOIDCProviderStatus, + "Duplicate issuer", + ); err != nil { + errs.add(fmt.Errorf("could not update status: %w", err)) + } continue } + + issuerURL, err := url.Parse(opc.Spec.Issuer) + if err != nil { + if err := c.updateStatus( + ctx.Context, + opc.Namespace, + opc.Name, + configv1alpha1.InvalidOIDCProviderStatus, + "Invalid issuer URL: "+err.Error(), + ); err != nil { + errs.add(fmt.Errorf("could not update status: %w", err)) + } + continue + } + oidcProvider := &provider.OIDCProvider{Issuer: issuerURL} err = oidcProvider.Validate() if err != nil { - klog.InfoS( - "OIDCProviderConfigWatcherController Sync could failed to validate OIDCProviderConfig", - "err", - err, - ) + if err := c.updateStatus( + ctx.Context, + opc.Namespace, + opc.Name, + configv1alpha1.InvalidOIDCProviderStatus, + "Invalid issuer: "+err.Error(), + ); err != nil { + errs.add(fmt.Errorf("could not update status: %w", err)) + } continue } + oidcProviders = append(oidcProviders, oidcProvider) - klog.InfoS( - "OIDCProviderConfigWatcherController Sync accepted OIDCProviderConfig", - "issuer", - issuerURL, - ) + if err := c.updateStatus( + ctx.Context, + opc.Namespace, + opc.Name, + configv1alpha1.SuccessOIDCProviderStatus, + "Provider successfully created", + ); err != nil { + // errs.add(fmt.Errorf("could not update status: %w", err)) + return fmt.Errorf("could not update status: %w", err) + } } c.providerSetter.SetProviders(oidcProviders...) + + return errs.errOrNil() +} + +func (c *oidcProviderConfigWatcherController) updateStatus( + ctx context.Context, + namespace, name string, + status configv1alpha1.OIDCProviderStatus, + message string, +) error { + return retry.RetryOnConflict(retry.DefaultRetry, func() error { + opc, err := c.client.ConfigV1alpha1().OIDCProviderConfigs(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("get failed: %w", err) + } + + if opc.Status.Status == status && opc.Status.Message == message { + return nil + } + + klog.InfoS( + "attempting status update", + "openidproviderconfig", + klog.KRef(namespace, name), + "status", + status, + "message", + message, + ) + opc.Status.Status = status + opc.Status.Message = message + _, err = c.client.ConfigV1alpha1().OIDCProviderConfigs(namespace).Update(ctx, opc, metav1.UpdateOptions{}) + return err + }) +} + +type multiError []error + +func newMultiError() multiError { + return make([]error, 0) +} + +func (m *multiError) add(err error) { + *m = append(*m, err) +} + +func (m multiError) len() int { + return len(m) +} + +func (m multiError) Error() string { + sb := strings.Builder{} + fmt.Fprintf(&sb, "%d errors:", m.len()) + for _, err := range m { + fmt.Fprintf(&sb, "\n- %s", err.Error()) + } + return sb.String() +} + +func (m multiError) errOrNil() error { + if m.len() > 0 { + return m + } return nil } diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 148e1878d..94f3cd9d4 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -59,21 +59,43 @@ func TestSupervisorOIDCDiscovery(t *testing.T) { issuer2 := fmt.Sprintf("http://%s/nested/issuer2", env.SupervisorAddress) issuer3 := fmt.Sprintf("http://%s/issuer3", env.SupervisorAddress) issuer4 := fmt.Sprintf("http://%s/issuer4", env.SupervisorAddress) + issuer5 := fmt.Sprintf("http://%s/issuer5", env.SupervisorAddress) + badIssuer := fmt.Sprintf("http://%s/badIssuer?cannot-use=queries", env.SupervisorAddress) // When OIDCProviderConfig are created in sequence they each cause a discovery endpoint to appear only for as long as the OIDCProviderConfig exists. - createdOIDCProviderConfig1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer1, "from-integration-test1") - requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig1, client, ns, issuer1) - createdOIDCProviderConfig2 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer2, "from-integration-test2") - requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig2, client, ns, issuer2) + config1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer1, "from-integration-test1") + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config1, client, ns, issuer1) + config2 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer2, "from-integration-test2") + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config2, client, ns, issuer2) // When multiple OIDCProviderConfigs exist at the same time they each serve a unique discovery endpoint. - createdOIDCProviderConfig3 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer3, "from-integration-test3") - createdOIDCProviderConfig4 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer4, "from-integration-test4") + config3 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer3, "from-integration-test3") + config4 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer4, "from-integration-test4") requireWellKnownEndpointIsWorking(t, issuer3) // discovery for issuer3 is still working after issuer4 started working // When they are deleted they stop serving discovery endpoints. - requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig3, client, ns, issuer2) - requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, createdOIDCProviderConfig4, client, ns, issuer2) + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config3, client, ns, issuer3) + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config4, client, ns, issuer4) + + // When the same issuer is added twice, both issuers are marked as duplicates, and neither provider is serving. + config5Duplicate1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer5, "from-integration-test5") + config5Duplicate2 := createOIDCProviderConfig(t, "from-integration-test5-duplicate", client, ns, issuer5) + requireStatus(t, client, ns, config5Duplicate1.Name, v1alpha1.DuplicateOIDCProviderStatus) + requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.DuplicateOIDCProviderStatus) + requireDiscoveryEndpointIsNotFound(t, issuer5) + + // If we delete the first duplicate issuer, the second duplicate issuer starts serving. + requireDelete(t, client, ns, config5Duplicate1.Name) + requireWellKnownEndpointIsWorking(t, issuer5) + requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.SuccessOIDCProviderStatus) + + // When we finally delete all issuers, the endpoint should be down. + requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config5Duplicate2, client, ns, issuer5) + + // When we create a provider with an invalid issuer, the status is set to invalid. + badConfig := createOIDCProviderConfig(t, "from-integration-test6", client, ns, badIssuer) + requireStatus(t, client, ns, badConfig.Name, v1alpha1.InvalidOIDCProviderStatus) + requireDiscoveryEndpointIsNotFound(t, badIssuer) } func requireDiscoveryEndpointIsNotFound(t *testing.T, issuerName string) { @@ -104,6 +126,7 @@ func requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t *testing t.Helper() newOIDCProviderConfig := createOIDCProviderConfig(t, oidcProviderConfigName, client, ns, issuerName) requireWellKnownEndpointIsWorking(t, issuerName) + requireStatus(t, client, ns, oidcProviderConfigName, v1alpha1.SuccessOIDCProviderStatus) return newOIDCProviderConfig } @@ -205,3 +228,27 @@ func createOIDCProviderConfig(t *testing.T, oidcProviderConfigName string, clien return createdOIDCProviderConfig } + +func requireDelete(t *testing.T, client pinnipedclientset.Interface, ns, name string) { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(ctx, name, metav1.DeleteOptions{}) + require.NoError(t, err) +} + +func requireStatus(t *testing.T, client pinnipedclientset.Interface, ns, name string, status v1alpha1.OIDCProviderStatus) { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + var opc *v1alpha1.OIDCProviderConfig + var err error + assert.Eventually(t, func() bool { + opc, err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Get(ctx, name, metav1.GetOptions{}) + return err == nil && opc.Status.Status == status + }, 10*time.Second, 200*time.Millisecond) + require.NoError(t, err) + require.Equalf(t, status, opc.Status.Status, "unexpected status (message = '%s')", opc.Status.Message) +} From 8b7d96f42cb0192e6c105087d9ec682b555014df Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Thu, 8 Oct 2020 11:28:21 -0700 Subject: [PATCH 16/28] Several small refactors related to OIDC providers --- cmd/pinniped-supervisor/main.go | 6 +- .../oidcproviderconfig_watcher.go | 66 +++---------------- internal/multierror/multierror.go | 39 +++++++++++ internal/oidc/discovery/discovery.go | 9 +-- internal/oidc/discovery/discovery_test.go | 8 +-- internal/oidc/oidc.go | 5 +- .../oidc/provider/{ => manager}/manager.go | 32 ++++----- internal/oidc/provider/oidcprovider.go | 52 +++++++++++---- internal/oidc/provider/oidcprovider_test.go | 33 ++++------ test/integration/supervisor_discovery_test.go | 4 +- 10 files changed, 134 insertions(+), 120 deletions(-) create mode 100644 internal/multierror/multierror.go rename internal/oidc/provider/{ => manager}/manager.go (78%) diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index dbbf5cfcf..fe04cd7f0 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -24,7 +24,7 @@ import ( "go.pinniped.dev/internal/controller/supervisorconfig" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/downward" - "go.pinniped.dev/internal/oidc/provider" + "go.pinniped.dev/internal/oidc/provider/manager" ) const ( @@ -61,7 +61,7 @@ func waitForSignal() os.Signal { func startControllers( ctx context.Context, - issuerProvider *provider.Manager, + issuerProvider *manager.Manager, pinnipedClient pinnipedclientset.Interface, pinnipedInformers pinnipedinformers.SharedInformerFactory, ) { @@ -114,7 +114,7 @@ func run(serverInstallationNamespace string) error { pinnipedinformers.WithNamespace(serverInstallationNamespace), ) - oidProvidersManager := provider.NewManager(http.NotFoundHandler()) + oidProvidersManager := manager.NewManager(http.NotFoundHandler()) startControllers(ctx, oidProvidersManager, pinnipedClient, pinnipedInformers) //nolint: gosec // Intentionally binding to all network interfaces. diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go index cd827c949..3f246a85a 100644 --- a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go @@ -6,8 +6,8 @@ package supervisorconfig import ( "context" "fmt" - "net/url" - "strings" + + "go.pinniped.dev/internal/multierror" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -73,10 +73,10 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er issuerCounts := make(map[string]int) for _, opc := range all { - issuerCounts[opc.Spec.Issuer] = issuerCounts[opc.Spec.Issuer] + 1 + issuerCounts[opc.Spec.Issuer]++ } - errs := newMultiError() + errs := multierror.New() oidcProviders := make([]*provider.OIDCProvider, 0) for _, opc := range all { @@ -88,36 +88,21 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er configv1alpha1.DuplicateOIDCProviderStatus, "Duplicate issuer", ); err != nil { - errs.add(fmt.Errorf("could not update status: %w", err)) + errs.Add(fmt.Errorf("could not update status: %w", err)) } continue } - issuerURL, err := url.Parse(opc.Spec.Issuer) + oidcProvider, err := provider.NewOIDCProvider(opc.Spec.Issuer) if err != nil { if err := c.updateStatus( ctx.Context, opc.Namespace, opc.Name, configv1alpha1.InvalidOIDCProviderStatus, - "Invalid issuer URL: "+err.Error(), + "Invalid: "+err.Error(), ); err != nil { - errs.add(fmt.Errorf("could not update status: %w", err)) - } - continue - } - - oidcProvider := &provider.OIDCProvider{Issuer: issuerURL} - err = oidcProvider.Validate() - if err != nil { - if err := c.updateStatus( - ctx.Context, - opc.Namespace, - opc.Name, - configv1alpha1.InvalidOIDCProviderStatus, - "Invalid issuer: "+err.Error(), - ); err != nil { - errs.add(fmt.Errorf("could not update status: %w", err)) + errs.Add(fmt.Errorf("could not update status: %w", err)) } continue } @@ -130,14 +115,13 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er configv1alpha1.SuccessOIDCProviderStatus, "Provider successfully created", ); err != nil { - // errs.add(fmt.Errorf("could not update status: %w", err)) - return fmt.Errorf("could not update status: %w", err) + errs.Add(fmt.Errorf("could not update status: %w", err)) } } c.providerSetter.SetProviders(oidcProviders...) - return errs.errOrNil() + return errs.ErrOrNil() } func (c *oidcProviderConfigWatcherController) updateStatus( @@ -171,33 +155,3 @@ func (c *oidcProviderConfigWatcherController) updateStatus( return err }) } - -type multiError []error - -func newMultiError() multiError { - return make([]error, 0) -} - -func (m *multiError) add(err error) { - *m = append(*m, err) -} - -func (m multiError) len() int { - return len(m) -} - -func (m multiError) Error() string { - sb := strings.Builder{} - fmt.Fprintf(&sb, "%d errors:", m.len()) - for _, err := range m { - fmt.Fprintf(&sb, "\n- %s", err.Error()) - } - return sb.String() -} - -func (m multiError) errOrNil() error { - if m.len() > 0 { - return m - } - return nil -} diff --git a/internal/multierror/multierror.go b/internal/multierror/multierror.go new file mode 100644 index 000000000..f9d9f2f88 --- /dev/null +++ b/internal/multierror/multierror.go @@ -0,0 +1,39 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package multierror + +import ( + "fmt" + "strings" +) + +type multiError []error + +func New() multiError { //nolint:golint // returning a private type for encapsulation purposes + return make([]error, 0) +} + +func (m *multiError) Add(err error) { + *m = append(*m, err) +} + +func (m multiError) len() int { + return len(m) +} + +func (m multiError) Error() string { + sb := strings.Builder{} + _, _ = fmt.Fprintf(&sb, "%d errors:", m.len()) + for _, err := range m { + _, _ = fmt.Fprintf(&sb, "\n- %s", err.Error()) + } + return sb.String() +} + +func (m multiError) ErrOrNil() error { + if m.len() > 0 { + return m + } + return nil +} diff --git a/internal/oidc/discovery/discovery.go b/internal/oidc/discovery/discovery.go index 84ca1c77f..c919e1501 100644 --- a/internal/oidc/discovery/discovery.go +++ b/internal/oidc/discovery/discovery.go @@ -6,8 +6,9 @@ package discovery import ( "encoding/json" - "fmt" "net/http" + + "go.pinniped.dev/internal/oidc" ) // Metadata holds all fields (that we care about) from the OpenID Provider Metadata section in the @@ -50,9 +51,9 @@ func New(issuerURL string) http.Handler { oidcConfig := Metadata{ Issuer: issuerURL, - AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v0/auth", issuerURL), - TokenEndpoint: fmt.Sprintf("%s/oauth2/v0/token", issuerURL), - JWKSURI: fmt.Sprintf("%s/jwks.json", issuerURL), + AuthorizationEndpoint: issuerURL + oidc.AuthorizationEndpointPath, + TokenEndpoint: issuerURL + oidc.TokenEndpointPath, + JWKSURI: issuerURL + oidc.JWKSEndpointPath, ResponseTypesSupported: []string{"code"}, SubjectTypesSupported: []string{"public"}, IDTokenSigningAlgValuesSupported: []string{"RS256"}, diff --git a/internal/oidc/discovery/discovery_test.go b/internal/oidc/discovery/discovery_test.go index 14f2d9b62..e21b3c4ab 100644 --- a/internal/oidc/discovery/discovery_test.go +++ b/internal/oidc/discovery/discovery_test.go @@ -30,13 +30,13 @@ func TestDiscovery(t *testing.T) { name: "happy path", issuer: "https://some-issuer.com/some/path", method: http.MethodGet, - path: "/some/path" + oidc.WellKnownURLPath, + path: "/some/path" + oidc.WellKnownEndpointPath, wantStatus: http.StatusOK, wantContentType: "application/json", wantBody: &Metadata{ Issuer: "https://some-issuer.com/some/path", - AuthorizationEndpoint: "https://some-issuer.com/some/path/oauth2/v0/auth", - TokenEndpoint: "https://some-issuer.com/some/path/oauth2/v0/token", + AuthorizationEndpoint: "https://some-issuer.com/some/path/oauth2/authorize", + TokenEndpoint: "https://some-issuer.com/some/path/oauth2/token", JWKSURI: "https://some-issuer.com/some/path/jwks.json", ResponseTypesSupported: []string{"code"}, SubjectTypesSupported: []string{"public"}, @@ -51,7 +51,7 @@ func TestDiscovery(t *testing.T) { name: "bad method", issuer: "https://some-issuer.com", method: http.MethodPost, - path: oidc.WellKnownURLPath, + path: oidc.WellKnownEndpointPath, wantStatus: http.StatusMethodNotAllowed, wantBody: map[string]string{ "error": "Method not allowed (try GET)", diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go index 795c3d380..d78f199c6 100644 --- a/internal/oidc/oidc.go +++ b/internal/oidc/oidc.go @@ -5,5 +5,8 @@ package oidc const ( - WellKnownURLPath = "/.well-known/openid-configuration" + WellKnownEndpointPath = "/.well-known/openid-configuration" + AuthorizationEndpointPath = "/oauth2/authorize" + TokenEndpointPath = "/oauth2/token" //nolint:gosec // ignore lint warning that this is a credential + JWKSEndpointPath = "/jwks.json" ) diff --git a/internal/oidc/provider/manager.go b/internal/oidc/provider/manager/manager.go similarity index 78% rename from internal/oidc/provider/manager.go rename to internal/oidc/provider/manager/manager.go index a50a70950..0c11b5be4 100644 --- a/internal/oidc/provider/manager.go +++ b/internal/oidc/provider/manager/manager.go @@ -1,11 +1,10 @@ // Copyright 2020 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package provider +package manager import ( "net/http" - "net/url" "strings" "sync" @@ -13,6 +12,7 @@ import ( "go.pinniped.dev/internal/oidc" "go.pinniped.dev/internal/oidc/discovery" + "go.pinniped.dev/internal/oidc/provider" ) // Manager can manage multiple active OIDC providers. It acts as a request router for them. @@ -24,21 +24,17 @@ type Manager struct { nextHandler http.Handler // the next handler in a chain, called when this manager didn't know how to handle a request } -// New returns an empty Manager. +// NewManager returns an empty Manager. // nextHandler will be invoked for any requests that could not be handled by this manager's providers. func NewManager(nextHandler http.Handler) *Manager { return &Manager{providerHandlers: make(map[string]*providerHandler), nextHandler: nextHandler} } type providerHandler struct { - provider *OIDCProvider + provider *provider.OIDCProvider discoveryHandler http.Handler } -func (h *providerHandler) Issuer() *url.URL { - return h.provider.Issuer -} - // SetProviders adds or updates all the given providerHandlers using each provider's issuer string // as the name of the provider to decide if it is an add or update operation. // @@ -47,12 +43,12 @@ func (h *providerHandler) Issuer() *url.URL { // // This method assumes that all of the OIDCProvider arguments have already been validated // by someone else before they are passed to this method. -func (c *Manager) SetProviders(oidcProviders ...*OIDCProvider) { +func (c *Manager) SetProviders(oidcProviders ...*provider.OIDCProvider) { c.mu.Lock() defer c.mu.Unlock() // Add all of the incoming providers. for _, incomingProvider := range oidcProviders { - issuerString := incomingProvider.Issuer.String() + issuerString := incomingProvider.Issuer() c.providerHandlers[issuerString] = &providerHandler{ provider: incomingProvider, discoveryHandler: discovery.New(issuerString), @@ -70,9 +66,9 @@ func (c *Manager) SetProviders(oidcProviders ...*OIDCProvider) { // ServeHTTP implements the http.Handler interface. func (c *Manager) ServeHTTP(resp http.ResponseWriter, req *http.Request) { - providerHandler := c.findProviderHandlerByIssuerURL(req.Host, req.URL.Path) + providerHandler := c.findProviderHandlerByIssuer(req.Host, req.URL.Path) if providerHandler != nil { - if req.URL.Path == providerHandler.Issuer().Path+oidc.WellKnownURLPath { + if req.URL.Path == providerHandler.provider.IssuerPath()+oidc.WellKnownEndpointPath { providerHandler.discoveryHandler.ServeHTTP(resp, req) return // handled! } @@ -94,20 +90,20 @@ func (c *Manager) ServeHTTP(resp http.ResponseWriter, req *http.Request) { c.nextHandler.ServeHTTP(resp, req) } -func (c *Manager) findProviderHandlerByIssuerURL(host, path string) *providerHandler { +func (c *Manager) findProviderHandlerByIssuer(host, path string) *providerHandler { for _, providerHandler := range c.providerHandlers { - pi := providerHandler.Issuer() // TODO do we need to compare scheme? not sure how to get it from the http.Request object - if host == pi.Host && strings.HasPrefix(path, pi.Path) { // TODO probably need better logic here? also maybe needs some of the logic from inside ServeMux + // TODO probably need better logic here? also maybe needs some of the logic from inside ServeMux + if host == providerHandler.provider.IssuerHost() && strings.HasPrefix(path, providerHandler.provider.IssuerPath()) { return providerHandler } } return nil } -func findIssuerInListOfProviders(issuer string, oidcProviders []*OIDCProvider) bool { - for _, provider := range oidcProviders { - if provider.Issuer.String() == issuer { +func findIssuerInListOfProviders(issuer string, oidcProviders []*provider.OIDCProvider) bool { + for _, oidcProvider := range oidcProviders { + if oidcProvider.Issuer() == issuer { return true } } diff --git a/internal/oidc/provider/oidcprovider.go b/internal/oidc/provider/oidcprovider.go index c7c24de45..cc4271674 100644 --- a/internal/oidc/provider/oidcprovider.go +++ b/internal/oidc/provider/oidcprovider.go @@ -4,6 +4,7 @@ package provider import ( + "fmt" "net/url" "strings" @@ -12,39 +13,68 @@ import ( // OIDCProvider represents all of the settings and state for an OIDC provider. type OIDCProvider struct { - Issuer *url.URL + issuer string + issuerHost string + issuerPath string } -// Validate returns an error if there is anything wrong with the provider settings, or -// returns nil if there is nothing wrong with the settings. -func (p *OIDCProvider) Validate() error { - if p.Issuer == nil { - return constable.Error(`provider must have an issuer`) +func NewOIDCProvider(issuer string) (*OIDCProvider, error) { + p := OIDCProvider{issuer: issuer} + err := p.validate() + if err != nil { + return nil, err + } + return &p, nil +} + +func (p *OIDCProvider) validate() error { + if p.issuer == "" { + return constable.Error("provider must have an issuer") } - if p.Issuer.Scheme != "https" && p.removeMeAfterWeNoLongerNeedHTTPIssuerSupport(p.Issuer.Scheme) { + issuerURL, err := url.Parse(p.issuer) + if err != nil { + return fmt.Errorf("could not parse issuer as URL: %w", err) + } + + if issuerURL.Scheme != "https" && p.removeMeAfterWeNoLongerNeedHTTPIssuerSupport(issuerURL.Scheme) { return constable.Error(`issuer must have "https" scheme`) } - if p.Issuer.User != nil { + if issuerURL.User != nil { return constable.Error(`issuer must not have username or password`) } - if strings.HasSuffix(p.Issuer.Path, "/") { + if strings.HasSuffix(issuerURL.Path, "/") { return constable.Error(`issuer must not have trailing slash in path`) } - if p.Issuer.RawQuery != "" { + if issuerURL.RawQuery != "" { return constable.Error(`issuer must not have query`) } - if p.Issuer.Fragment != "" { + if issuerURL.Fragment != "" { return constable.Error(`issuer must not have fragment`) } + p.issuerHost = issuerURL.Host + p.issuerPath = issuerURL.Path + return nil } +func (p *OIDCProvider) Issuer() string { + return p.issuer +} + +func (p *OIDCProvider) IssuerHost() string { + return p.issuerHost +} + +func (p *OIDCProvider) IssuerPath() string { + return p.issuerPath +} + func (p *OIDCProvider) removeMeAfterWeNoLongerNeedHTTPIssuerSupport(scheme string) bool { return scheme != "http" } diff --git a/internal/oidc/provider/oidcprovider_test.go b/internal/oidc/provider/oidcprovider_test.go index 2da0e3a83..81204e284 100644 --- a/internal/oidc/provider/oidcprovider_test.go +++ b/internal/oidc/provider/oidcprovider_test.go @@ -4,7 +4,6 @@ package provider import ( - "net/url" "testing" "github.com/stretchr/testify/require" @@ -13,63 +12,62 @@ import ( func TestOIDCProviderValidations(t *testing.T) { tests := []struct { name string - issuer *url.URL + issuer string wantError string }{ { name: "provider must have an issuer", - issuer: nil, + issuer: "", wantError: "provider must have an issuer", }, { name: "no scheme", - issuer: must(url.Parse("tuna.com")), + issuer: "tuna.com", wantError: `issuer must have "https" scheme`, }, { name: "bad scheme", - issuer: must(url.Parse("ftp://tuna.com")), + issuer: "ftp://tuna.com", wantError: `issuer must have "https" scheme`, }, { name: "fragment", - issuer: must(url.Parse("https://tuna.com/fish#some-frag")), + issuer: "https://tuna.com/fish#some-frag", wantError: `issuer must not have fragment`, }, { name: "query", - issuer: must(url.Parse("https://tuna.com?some=query")), + issuer: "https://tuna.com?some=query", wantError: `issuer must not have query`, }, { name: "username", - issuer: must(url.Parse("https://username@tuna.com")), + issuer: "https://username@tuna.com", wantError: `issuer must not have username or password`, }, { name: "password", - issuer: must(url.Parse("https://username:password@tuna.com")), + issuer: "https://username:password@tuna.com", wantError: `issuer must not have username or password`, }, { name: "without path", - issuer: must(url.Parse("https://tuna.com")), + issuer: "https://tuna.com", }, { name: "with path", - issuer: must(url.Parse("https://tuna.com/fish/marlin")), + issuer: "https://tuna.com/fish/marlin", }, { name: "trailing slash in path", - issuer: must(url.Parse("https://tuna.com/")), + issuer: "https://tuna.com/", wantError: `issuer must not have trailing slash in path`, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - p := OIDCProvider{Issuer: tt.issuer} - err := p.Validate() + _, err := NewOIDCProvider(tt.issuer) if tt.wantError != "" { require.EqualError(t, err, tt.wantError) } else { @@ -78,10 +76,3 @@ func TestOIDCProviderValidations(t *testing.T) { }) } } - -func must(u *url.URL, err error) *url.URL { - if err != nil { - panic(err) - } - return u -} diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 94f3cd9d4..77dcaebf2 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -175,8 +175,8 @@ func requireWellKnownEndpointIsWorking(t *testing.T, issuerName string) { // Check that the response matches our expectations. expectedResultTemplate := here.Doc(`{ "issuer": "%s", - "authorization_endpoint": "%s/oauth2/v0/auth", - "token_endpoint": "%s/oauth2/v0/token", + "authorization_endpoint": "%s/oauth2/authorize", + "token_endpoint": "%s/oauth2/token", "token_endpoint_auth_methods_supported": ["client_secret_basic"], "token_endpoint_auth_signing_alg_values_supported": ["RS256"], "jwks_uri": "%s/jwks.json", From 05141592f8b84addc21373cab1125d6c810363b3 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Thu, 8 Oct 2020 14:40:56 -0700 Subject: [PATCH 17/28] Refactor provider.Manager - And also handle when an issuer's path is a subpath of another issuer Signed-off-by: Ryan Richard --- internal/oidc/provider/manager/manager.go | 96 +++++--------- .../oidc/provider/manager/manager_test.go | 123 ++++++++++++++++++ 2 files changed, 154 insertions(+), 65 deletions(-) create mode 100644 internal/oidc/provider/manager/manager_test.go diff --git a/internal/oidc/provider/manager/manager.go b/internal/oidc/provider/manager/manager.go index 0c11b5be4..0626c48c3 100644 --- a/internal/oidc/provider/manager/manager.go +++ b/internal/oidc/provider/manager/manager.go @@ -5,7 +5,6 @@ package manager import ( "net/http" - "strings" "sync" "k8s.io/klog/v2" @@ -20,19 +19,15 @@ import ( // It is thread-safe. type Manager struct { mu sync.RWMutex - providerHandlers map[string]*providerHandler // map of issuer name to providerHandler - nextHandler http.Handler // the next handler in a chain, called when this manager didn't know how to handle a request + providers []*provider.OIDCProvider + providerHandlers map[string]http.Handler // map of all routes for all providers + nextHandler http.Handler // the next handler in a chain, called when this manager didn't know how to handle a request } // NewManager returns an empty Manager. // nextHandler will be invoked for any requests that could not be handled by this manager's providers. func NewManager(nextHandler http.Handler) *Manager { - return &Manager{providerHandlers: make(map[string]*providerHandler), nextHandler: nextHandler} -} - -type providerHandler struct { - provider *provider.OIDCProvider - discoveryHandler http.Handler + return &Manager{providerHandlers: make(map[string]http.Handler), nextHandler: nextHandler} } // SetProviders adds or updates all the given providerHandlers using each provider's issuer string @@ -43,69 +38,40 @@ type providerHandler struct { // // This method assumes that all of the OIDCProvider arguments have already been validated // by someone else before they are passed to this method. -func (c *Manager) SetProviders(oidcProviders ...*provider.OIDCProvider) { - c.mu.Lock() - defer c.mu.Unlock() - // Add all of the incoming providers. +func (m *Manager) SetProviders(oidcProviders ...*provider.OIDCProvider) { + m.mu.Lock() + defer m.mu.Unlock() + + m.providers = oidcProviders + m.providerHandlers = make(map[string]http.Handler) + for _, incomingProvider := range oidcProviders { - issuerString := incomingProvider.Issuer() - c.providerHandlers[issuerString] = &providerHandler{ - provider: incomingProvider, - discoveryHandler: discovery.New(issuerString), - } - klog.InfoS("oidc provider manager added or updated issuer", "issuer", issuerString) - } - // Remove any providers that we previously handled but no longer exist. - for issuerKey := range c.providerHandlers { - if !findIssuerInListOfProviders(issuerKey, oidcProviders) { - delete(c.providerHandlers, issuerKey) - klog.InfoS("oidc provider manager removed issuer", "issuer", issuerKey) - } + m.providerHandlers[incomingProvider.IssuerHost()+"/"+incomingProvider.IssuerPath()+oidc.WellKnownEndpointPath] = discovery.New(incomingProvider.Issuer()) + klog.InfoS("oidc provider manager added or updated issuer", "issuer", incomingProvider.Issuer()) } } // ServeHTTP implements the http.Handler interface. -func (c *Manager) ServeHTTP(resp http.ResponseWriter, req *http.Request) { - providerHandler := c.findProviderHandlerByIssuer(req.Host, req.URL.Path) - if providerHandler != nil { - if req.URL.Path == providerHandler.provider.IssuerPath()+oidc.WellKnownEndpointPath { - providerHandler.discoveryHandler.ServeHTTP(resp, req) - return // handled! - } - klog.InfoS( - "oidc provider manager found issuer but could not handle request", - "method", req.Method, - "host", req.Host, - "path", req.URL.Path, - ) - } else { - klog.InfoS( - "oidc provider manager could not find issuer to handle request", - "method", req.Method, - "host", req.Host, - "path", req.URL.Path, - ) +func (m *Manager) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + requestHandler := m.findHandler(req) + + klog.InfoS( + "oidc provider manager examining request", + "method", req.Method, + "host", req.Host, + "path", req.URL.Path, + "foundMatchingIssuer", requestHandler != nil, + ) + + if requestHandler == nil { + requestHandler = m.nextHandler // couldn't find an issuer to handle the request } - // Didn't know how to handle this request, so send it along the chain for further processing. - c.nextHandler.ServeHTTP(resp, req) + requestHandler.ServeHTTP(resp, req) } -func (c *Manager) findProviderHandlerByIssuer(host, path string) *providerHandler { - for _, providerHandler := range c.providerHandlers { - // TODO do we need to compare scheme? not sure how to get it from the http.Request object - // TODO probably need better logic here? also maybe needs some of the logic from inside ServeMux - if host == providerHandler.provider.IssuerHost() && strings.HasPrefix(path, providerHandler.provider.IssuerPath()) { - return providerHandler - } - } - return nil -} +func (m *Manager) findHandler(req *http.Request) http.Handler { + m.mu.RLock() + defer m.mu.RUnlock() -func findIssuerInListOfProviders(issuer string, oidcProviders []*provider.OIDCProvider) bool { - for _, oidcProvider := range oidcProviders { - if oidcProvider.Issuer() == issuer { - return true - } - } - return false + return m.providerHandlers[req.Host+"/"+req.URL.Path] } diff --git a/internal/oidc/provider/manager/manager_test.go b/internal/oidc/provider/manager/manager_test.go new file mode 100644 index 000000000..c877f3acf --- /dev/null +++ b/internal/oidc/provider/manager/manager_test.go @@ -0,0 +1,123 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package manager + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/sclevine/spec" + "github.com/stretchr/testify/require" + + "go.pinniped.dev/internal/oidc" + "go.pinniped.dev/internal/oidc/discovery" + "go.pinniped.dev/internal/oidc/provider" +) + +func TestManager(t *testing.T) { + spec.Run(t, "ServeHTTP", func(t *testing.T, when spec.G, it spec.S) { + var r *require.Assertions + var subject *Manager + var nextHandler http.HandlerFunc + var fallbackHandlerWasCalled bool + + newGetRequest := func(url string) *http.Request { + return httptest.NewRequest(http.MethodGet, url, nil) + } + + requireDiscoveryRequestToBeHandled := func(issuer, requestURLSuffix string) { + recorder := httptest.NewRecorder() + + subject.ServeHTTP(recorder, newGetRequest(issuer+oidc.WellKnownEndpointPath+requestURLSuffix)) + + r.Equal(http.StatusOK, recorder.Code) + responseBody, err := ioutil.ReadAll(recorder.Body) + r.NoError(err) + parsedDiscoveryResult := discovery.Metadata{} + err = json.Unmarshal(responseBody, &parsedDiscoveryResult) + r.NoError(err) + + r.Equal(issuer, parsedDiscoveryResult.Issuer) + } + + it.Before(func() { + r = require.New(t) + nextHandler = func(http.ResponseWriter, *http.Request) { + fallbackHandlerWasCalled = true + } + subject = NewManager(nextHandler) + }) + + when("given no providers", func() { + it("sends all requests to the nextHandler", func() { + r.False(fallbackHandlerWasCalled) + subject.ServeHTTP(httptest.NewRecorder(), newGetRequest("/anything")) + r.True(fallbackHandlerWasCalled) + }) + }) + + when("given some valid providers", func() { + issuer1 := "https://example.com/some/path" + issuer2 := "https://example.com/some/path/more/deeply/nested/path" // note that this is a sub-path of the other issuer url + + it.Before(func() { + p1, err := provider.NewOIDCProvider(issuer1) + r.NoError(err) + p2, err := provider.NewOIDCProvider(issuer2) + r.NoError(err) + subject.SetProviders(p1, p2) + }) + + it("sends all non-matching host requests to the nextHandler", func() { + r.False(fallbackHandlerWasCalled) + url := strings.ReplaceAll(issuer1+oidc.WellKnownEndpointPath, "example.com", "wrong-host.com") + subject.ServeHTTP(httptest.NewRecorder(), newGetRequest(url)) + r.True(fallbackHandlerWasCalled) + }) + + it("sends all non-matching path requests to the nextHandler", func() { + r.False(fallbackHandlerWasCalled) + subject.ServeHTTP(httptest.NewRecorder(), newGetRequest("https://example.com/path-does-not-match-any-provider")) + r.True(fallbackHandlerWasCalled) + }) + + it("sends requests which match the issuer prefix but do not match any of that provider's known paths to the nextHandler", func() { + r.False(fallbackHandlerWasCalled) + subject.ServeHTTP(httptest.NewRecorder(), newGetRequest(issuer1+"/unhandled-sub-path")) + r.True(fallbackHandlerWasCalled) + }) + + it("routes matching requests to the appropriate provider", func() { + requireDiscoveryRequestToBeHandled(issuer1, "") + requireDiscoveryRequestToBeHandled(issuer2, "") + requireDiscoveryRequestToBeHandled(issuer2, "?some=query") + r.False(fallbackHandlerWasCalled) + }) + }) + + when("given the same valid providers in reverse order", func() { + issuer1 := "https://example.com/some/path" + issuer2 := "https://example.com/some/path/more/deeply/nested/path" + + it.Before(func() { + p1, err := provider.NewOIDCProvider(issuer1) + r.NoError(err) + p2, err := provider.NewOIDCProvider(issuer2) + r.NoError(err) + subject.SetProviders(p2, p1) + }) + + it("still routes matching requests to the appropriate provider", func() { + requireDiscoveryRequestToBeHandled(issuer1, "") + requireDiscoveryRequestToBeHandled(issuer2, "") + requireDiscoveryRequestToBeHandled(issuer2, "?some=query") + r.False(fallbackHandlerWasCalled) + }) + }) + }) +} From b74486f30535264835eab3f4d151f074e147b1ca Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Thu, 8 Oct 2020 17:40:58 -0700 Subject: [PATCH 18/28] Start back-filling unit tests for OIDCProviderConfigWatcherController - Left some TODOs for more things that it should test --- .../oidcproviderconfig_watcher_test.go | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go new file mode 100644 index 000000000..6b76280b5 --- /dev/null +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go @@ -0,0 +1,197 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package supervisorconfig + +import ( + "context" + "testing" + "time" + + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/clock" + + "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" + pinnipedfake "go.pinniped.dev/generated/1.19/client/clientset/versioned/fake" + pinnipedinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions" + "go.pinniped.dev/internal/controllerlib" + "go.pinniped.dev/internal/oidc/provider" + "go.pinniped.dev/internal/testutil" +) + +func TestInformerFilters(t *testing.T) { + spec.Run(t, "informer filters", func(t *testing.T, when spec.G, it spec.S) { + var r *require.Assertions + var observableWithInformerOption *testutil.ObservableWithInformerOption + var configMapInformerFilter controllerlib.Filter + + it.Before(func() { + r = require.New(t) + observableWithInformerOption = testutil.NewObservableWithInformerOption() + opcInformer := pinnipedinformers.NewSharedInformerFactoryWithOptions(nil, 0).Config().V1alpha1().OIDCProviderConfigs() + _ = NewOIDCProviderConfigWatcherController( + nil, + nil, + nil, + opcInformer, + observableWithInformerOption.WithInformer, // make it possible to observe the behavior of the Filters + ) + configMapInformerFilter = observableWithInformerOption.GetFilterForInformer(opcInformer) + }) + + when("watching OIDCProviderConfig objects", func() { + var subject controllerlib.Filter + var target, otherNamespace, otherName *v1alpha1.OIDCProviderConfig + + it.Before(func() { + subject = configMapInformerFilter + target = &v1alpha1.OIDCProviderConfig{ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "some-namespace"}} + otherNamespace = &v1alpha1.OIDCProviderConfig{ObjectMeta: metav1.ObjectMeta{Name: "some-name", Namespace: "other-namespace"}} + otherName = &v1alpha1.OIDCProviderConfig{ObjectMeta: metav1.ObjectMeta{Name: "other-name", Namespace: "some-namespace"}} + }) + + when("any OIDCProviderConfig changes", func() { + it("returns true to trigger the sync method", func() { + r.True(subject.Add(target)) + r.True(subject.Add(otherName)) + r.True(subject.Add(otherNamespace)) + r.True(subject.Update(target, otherName)) + r.True(subject.Update(otherName, otherName)) + r.True(subject.Update(otherNamespace, otherName)) + r.True(subject.Update(otherName, target)) + r.True(subject.Update(otherName, otherName)) + r.True(subject.Update(otherName, otherNamespace)) + r.True(subject.Delete(target)) + r.True(subject.Delete(otherName)) + r.True(subject.Delete(otherNamespace)) + }) + }) + }) + }, spec.Parallel(), spec.Report(report.Terminal{})) +} + +type fakeProvidersSetter struct { + SetProvidersWasCalled bool + OIDCProvidersReceived []*provider.OIDCProvider +} + +func (f *fakeProvidersSetter) SetProviders(oidcProviders ...*provider.OIDCProvider) { + f.SetProvidersWasCalled = true + f.OIDCProvidersReceived = oidcProviders +} + +func TestSync(t *testing.T) { + spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) { + var r *require.Assertions + + var subject controllerlib.Controller + var opcInformerClient *pinnipedfake.Clientset + var opcInformers pinnipedinformers.SharedInformerFactory + var pinnipedAPIClient *pinnipedfake.Clientset + var timeoutContext context.Context + var timeoutContextCancel context.CancelFunc + var syncContext *controllerlib.Context + var frozenNow time.Time + var providersSetter *fakeProvidersSetter + + // Defer starting the informers until the last possible moment so that the + // nested Before's can keep adding things to the informer caches. + var startInformersAndController = func() { + // Set this at the last second to allow for injection of server override. + subject = NewOIDCProviderConfigWatcherController( + providersSetter, + clock.NewFakeClock(frozenNow), + pinnipedAPIClient, + opcInformers.Config().V1alpha1().OIDCProviderConfigs(), + controllerlib.WithInformer, + ) + + // Set this at the last second to support calling subject.Name(). + syncContext = &controllerlib.Context{ + Context: timeoutContext, + Name: subject.Name(), + Key: controllerlib.Key{ + Namespace: "some-namespace", + Name: "config-name", + }, + } + + // Must start informers before calling TestRunSynchronously() + opcInformers.Start(timeoutContext.Done()) + controllerlib.TestRunSynchronously(t, subject) + } + + it.Before(func() { + r = require.New(t) + + providersSetter = &fakeProvidersSetter{} + frozenNow = time.Date(2020, time.September, 23, 7, 42, 0, 0, time.Local) + + timeoutContext, timeoutContextCancel = context.WithTimeout(context.Background(), time.Second*3) + + opcInformerClient = pinnipedfake.NewSimpleClientset() + opcInformers = pinnipedinformers.NewSharedInformerFactory(opcInformerClient, 0) + pinnipedAPIClient = pinnipedfake.NewSimpleClientset() + }) + + it.After(func() { + timeoutContextCancel() + }) + + when("there are some valid OIDCProviderConfigs in the informer", func() { + it.Before(func() { + oidcProviderConfig1 := &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: "some-namespace"}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer1.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig1)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig1)) + + oidcProviderConfig2 := &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "config2", Namespace: "some-namespace"}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer2.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig2)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig2)) + }) + + it("calls the ProvidersSetter and updates the OIDCProviderConfigs", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.Len(providersSetter.OIDCProvidersReceived, 2) + + // TODO make more assertions about the OIDCProvidersReceived + // TODO make assertions about the expected pinnipedAPIClient.Actions() + }) + + when("there is a conflict while updating an OIDCProviderConfig", func() { + // TODO write this test + }) + }) + + when("there are both valid and invalid OIDCProviderConfigs in the informer", func() { + // TODO write this test + }) + + when("they there are OIDCProviderConfigs with duplicate issuer names in the informer", func() { + // TODO write this test + }) + + when("there are no OIDCProviderConfigs in the informer", func() { + it("keeps waiting for one", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + r.Empty(pinnipedAPIClient.Actions()) + r.True(providersSetter.SetProvidersWasCalled) + r.Empty(providersSetter.OIDCProvidersReceived) + }) + }) + }, spec.Parallel(), spec.Report(report.Terminal{})) +} From fac4d074d0f625daf2b99e074860cf87d2e3fcbf Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Fri, 9 Oct 2020 08:00:33 -0400 Subject: [PATCH 19/28] internal/multierror: add tests Signed-off-by: Andrew Keesler --- internal/multierror/multierror.go | 34 +++++++++++++++++--------- internal/multierror/multierror_test.go | 24 ++++++++++++++++++ 2 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 internal/multierror/multierror_test.go diff --git a/internal/multierror/multierror.go b/internal/multierror/multierror.go index f9d9f2f88..c9eab6b98 100644 --- a/internal/multierror/multierror.go +++ b/internal/multierror/multierror.go @@ -1,6 +1,15 @@ // Copyright 2020 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +// Package multierror provides a type that can translate multiple errors into a Go error interface. +// +// A common use of this package is as follows. +// errs := multierror.New() +// for i := range stuff { +// err := doThing(i) +// errs.Add(err) +// } +// return errs.ErrOrNil() package multierror import ( @@ -8,31 +17,34 @@ import ( "strings" ) -type multiError []error +// MultiError holds a list of error's, that could potentially be empty. +// +// Use New() to create a MultiError. +type MultiError []error -func New() multiError { //nolint:golint // returning a private type for encapsulation purposes +// New returns an empty MultiError. +func New() MultiError { return make([]error, 0) } -func (m *multiError) Add(err error) { +// Add adds an error to the MultiError. The provided err must not be nil. +func (m *MultiError) Add(err error) { *m = append(*m, err) } -func (m multiError) len() int { - return len(m) -} - -func (m multiError) Error() string { +// Error implements the error.Error() interface method. +func (m MultiError) Error() string { sb := strings.Builder{} - _, _ = fmt.Fprintf(&sb, "%d errors:", m.len()) + _, _ = fmt.Fprintf(&sb, "%d error(s):", len(m)) for _, err := range m { _, _ = fmt.Fprintf(&sb, "\n- %s", err.Error()) } return sb.String() } -func (m multiError) ErrOrNil() error { - if m.len() > 0 { +// ErrOrNil returns either nil, if there are no errors in this MultiError, or an error, otherwise. +func (m MultiError) ErrOrNil() error { + if len(m) > 0 { return m } return nil diff --git a/internal/multierror/multierror_test.go b/internal/multierror/multierror_test.go new file mode 100644 index 000000000..cb96771e5 --- /dev/null +++ b/internal/multierror/multierror_test.go @@ -0,0 +1,24 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package multierror + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMultierror(t *testing.T) { + errs := New() + + require.Nil(t, errs.ErrOrNil()) + + errs.Add(errors.New("some error 1")) + require.EqualError(t, errs.ErrOrNil(), "1 error(s):\n- some error 1") + + errs.Add(errors.New("some error 2")) + errs.Add(errors.New("some error 3")) + require.EqualError(t, errs.ErrOrNil(), "3 error(s):\n- some error 1\n- some error 2\n- some error 3") +} From bb015adf4e374dc3b60bd06e9006f1017d2b6cf5 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Fri, 9 Oct 2020 10:39:17 -0400 Subject: [PATCH 20/28] Backfill tests to OIDCProviderConfig controller Signed-off-by: Andrew Keesler --- .../oidcproviderconfig_watcher.go | 5 +- .../oidcproviderconfig_watcher_test.go | 632 +++++++++++++++++- internal/multierror/multierror.go | 20 +- 3 files changed, 637 insertions(+), 20 deletions(-) diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go index 3f246a85a..3f21d5fe6 100644 --- a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go @@ -86,7 +86,7 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er opc.Namespace, opc.Name, configv1alpha1.DuplicateOIDCProviderStatus, - "Duplicate issuer", + "Duplicate issuer: "+opc.Spec.Issuer, ); err != nil { errs.Add(fmt.Errorf("could not update status: %w", err)) } @@ -107,7 +107,6 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er continue } - oidcProviders = append(oidcProviders, oidcProvider) if err := c.updateStatus( ctx.Context, opc.Namespace, @@ -116,7 +115,9 @@ func (c *oidcProviderConfigWatcherController) Sync(ctx controllerlib.Context) er "Provider successfully created", ); err != nil { errs.Add(fmt.Errorf("could not update status: %w", err)) + continue } + oidcProviders = append(oidcProviders, oidcProvider) } c.providerSetter.SetProviders(oidcProviders...) diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go index 6b76280b5..a7f549a86 100644 --- a/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go @@ -5,19 +5,27 @@ package supervisorconfig import ( "context" + "errors" + "reflect" + "sync" "testing" "time" "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/stretchr/testify/require" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/clock" + coretesting "k8s.io/client-go/testing" "go.pinniped.dev/generated/1.19/apis/config/v1alpha1" pinnipedfake "go.pinniped.dev/generated/1.19/client/clientset/versioned/fake" pinnipedinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions" "go.pinniped.dev/internal/controllerlib" + "go.pinniped.dev/internal/here" "go.pinniped.dev/internal/oidc/provider" "go.pinniped.dev/internal/testutil" ) @@ -85,6 +93,8 @@ func (f *fakeProvidersSetter) SetProviders(oidcProviders ...*provider.OIDCProvid func TestSync(t *testing.T) { spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) { + const namespace = "some-namespace" + var r *require.Assertions var subject controllerlib.Controller @@ -96,6 +106,7 @@ func TestSync(t *testing.T) { var syncContext *controllerlib.Context var frozenNow time.Time var providersSetter *fakeProvidersSetter + var oidcProviderConfigGVR schema.GroupVersionResource // Defer starting the informers until the last possible moment so that the // nested Before's can keep adding things to the informer caches. @@ -114,7 +125,7 @@ func TestSync(t *testing.T) { Context: timeoutContext, Name: subject.Name(), Key: controllerlib.Key{ - Namespace: "some-namespace", + Namespace: namespace, Name: "config-name", }, } @@ -135,6 +146,12 @@ func TestSync(t *testing.T) { opcInformerClient = pinnipedfake.NewSimpleClientset() opcInformers = pinnipedinformers.NewSharedInformerFactory(opcInformerClient, 0) pinnipedAPIClient = pinnipedfake.NewSimpleClientset() + + oidcProviderConfigGVR = schema.GroupVersionResource{ + Group: v1alpha1.SchemeGroupVersion.Group, + Version: v1alpha1.SchemeGroupVersion.Version, + Resource: "oidcproviderconfigs", + } }) it.After(func() { @@ -142,45 +159,632 @@ func TestSync(t *testing.T) { }) when("there are some valid OIDCProviderConfigs in the informer", func() { + var ( + oidcProviderConfig1 *v1alpha1.OIDCProviderConfig + oidcProviderConfig2 *v1alpha1.OIDCProviderConfig + ) + it.Before(func() { - oidcProviderConfig1 := &v1alpha1.OIDCProviderConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: "some-namespace"}, + oidcProviderConfig1 = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace}, Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer1.com"}, } r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig1)) r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig1)) - oidcProviderConfig2 := &v1alpha1.OIDCProviderConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "config2", Namespace: "some-namespace"}, + oidcProviderConfig2 = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "config2", Namespace: namespace}, Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer2.com"}, } r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig2)) r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig2)) }) - it("calls the ProvidersSetter and updates the OIDCProviderConfigs", func() { + it("calls the ProvidersSetter", func() { startInformersAndController() err := controllerlib.TestSync(t, subject, *syncContext) r.NoError(err) - r.True(providersSetter.SetProvidersWasCalled) - r.Len(providersSetter.OIDCProvidersReceived, 2) + provider1, err := provider.NewOIDCProvider(oidcProviderConfig1.Spec.Issuer) + r.NoError(err) - // TODO make more assertions about the OIDCProvidersReceived - // TODO make assertions about the expected pinnipedAPIClient.Actions() + provider2, err := provider.NewOIDCProvider(oidcProviderConfig2.Spec.Issuer) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.ElementsMatch( + []*provider.OIDCProvider{ + provider1, + provider2, + }, + providersSetter.OIDCProvidersReceived, + ) + }) + + it("updates the status to success in the OIDCProviderConfigs", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + oidcProviderConfig1.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig1.Status.Message = "Provider successfully created" + + oidcProviderConfig2.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig2.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig1.Namespace, + oidcProviderConfig1.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig1.Namespace, + oidcProviderConfig1, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig2.Namespace, + oidcProviderConfig2.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig2.Namespace, + oidcProviderConfig2, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + + when("one OIDCProviderConfig is already up to date", func() { + it.Before(func() { + oidcProviderConfig1.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig1.Status.Message = "Provider successfully created" + + r.NoError(pinnipedAPIClient.Tracker().Update(oidcProviderConfigGVR, oidcProviderConfig1, oidcProviderConfig1.Namespace)) + r.NoError(opcInformerClient.Tracker().Update(oidcProviderConfigGVR, oidcProviderConfig1, oidcProviderConfig1.Namespace)) + }) + + it("only updates the out-of-date OIDCProviderConfig", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + oidcProviderConfig2.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig2.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig1.Namespace, + oidcProviderConfig1.Name, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig2.Namespace, + oidcProviderConfig2.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig2.Namespace, + oidcProviderConfig2, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + + it("calls the ProvidersSetter with both OIDCProviderConfig's", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + provider1, err := provider.NewOIDCProvider(oidcProviderConfig1.Spec.Issuer) + r.NoError(err) + + provider2, err := provider.NewOIDCProvider(oidcProviderConfig2.Spec.Issuer) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.ElementsMatch( + []*provider.OIDCProvider{ + provider1, + provider2, + }, + providersSetter.OIDCProvidersReceived, + ) + }) + }) + + when("updating only one OIDCProviderConfig fails for a reason other than conflict", func() { + it.Before(func() { + once := sync.Once{} + pinnipedAPIClient.PrependReactor( + "update", + "oidcproviderconfigs", + func(_ coretesting.Action) (bool, runtime.Object, error) { + var err error + once.Do(func() { + err = errors.New("some update error") + }) + return true, nil, err + }, + ) + }) + + it("sets the provider that it could actually update in the API", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, "1 error(s):\n- could not update status: some update error") + + provider1, err := provider.NewOIDCProvider(oidcProviderConfig1.Spec.Issuer) + r.NoError(err) + + provider2, err := provider.NewOIDCProvider(oidcProviderConfig2.Spec.Issuer) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.Len(providersSetter.OIDCProvidersReceived, 1) + r.True( + reflect.DeepEqual(providersSetter.OIDCProvidersReceived[0], provider1) || + reflect.DeepEqual(providersSetter.OIDCProvidersReceived[0], provider2), + ) + }) + + it("returns an error", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, "1 error(s):\n- could not update status: some update error") + + oidcProviderConfig1.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig1.Status.Message = "Provider successfully created" + + oidcProviderConfig2.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig2.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig1.Namespace, + oidcProviderConfig1.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig1.Namespace, + oidcProviderConfig1, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig2.Namespace, + oidcProviderConfig2.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig2.Namespace, + oidcProviderConfig2, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + }) + }) + + when("there are errors updating the OIDCProviderConfigs", func() { + var ( + oidcProviderConfig *v1alpha1.OIDCProviderConfig + ) + + it.Before(func() { + oidcProviderConfig = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "config", Namespace: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig)) }) when("there is a conflict while updating an OIDCProviderConfig", func() { - // TODO write this test + it.Before(func() { + once := sync.Once{} + pinnipedAPIClient.PrependReactor( + "update", + "oidcproviderconfigs", + func(_ coretesting.Action) (bool, runtime.Object, error) { + var err error + once.Do(func() { + err = k8serrors.NewConflict(schema.GroupResource{}, "", nil) + }) + return true, nil, err + }, + ) + }) + + it("retries updating the OIDCProviderConfig", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig, + ), + } + r.Equal(expectedActions, pinnipedAPIClient.Actions()) + }) + }) + + when("updating the OIDCProviderConfig fails for a reason other than conflict", func() { + it.Before(func() { + pinnipedAPIClient.PrependReactor( + "update", + "oidcproviderconfigs", + func(_ coretesting.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("some update error") + }, + ) + }) + + it("returns an error", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, "1 error(s):\n- could not update status: some update error") + + oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig, + ), + } + r.Equal(expectedActions, pinnipedAPIClient.Actions()) + }) + }) + + when("there is an error when getting the OIDCProviderConfig", func() { + it.Before(func() { + pinnipedAPIClient.PrependReactor( + "get", + "oidcproviderconfigs", + func(_ coretesting.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("some get error") + }, + ) + }) + + it("returns the get error", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, "1 error(s):\n- could not update status: get failed: some get error") + + oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig.Name, + ), + } + r.Equal(expectedActions, pinnipedAPIClient.Actions()) + }) }) }) when("there are both valid and invalid OIDCProviderConfigs in the informer", func() { - // TODO write this test + var ( + validOIDCProviderConfig *v1alpha1.OIDCProviderConfig + invalidOIDCProviderConfig *v1alpha1.OIDCProviderConfig + ) + + it.Before(func() { + validOIDCProviderConfig = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "valid-config", Namespace: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://valid-issuer.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(validOIDCProviderConfig)) + r.NoError(opcInformerClient.Tracker().Add(validOIDCProviderConfig)) + + invalidOIDCProviderConfig = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "invalid-config", Namespace: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://invalid-issuer.com?some=query"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(invalidOIDCProviderConfig)) + r.NoError(opcInformerClient.Tracker().Add(invalidOIDCProviderConfig)) + }) + + it("calls the ProvidersSetter with the valid provider", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + validProvider, err := provider.NewOIDCProvider(validOIDCProviderConfig.Spec.Issuer) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.Equal( + []*provider.OIDCProvider{ + validProvider, + }, + providersSetter.OIDCProvidersReceived, + ) + }) + + it("updates the status to success/invalid in the OIDCProviderConfigs", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + validOIDCProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + validOIDCProviderConfig.Status.Message = "Provider successfully created" + + invalidOIDCProviderConfig.Status.Status = v1alpha1.InvalidOIDCProviderStatus + invalidOIDCProviderConfig.Status.Message = "Invalid: issuer must not have query" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + invalidOIDCProviderConfig.Namespace, + invalidOIDCProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + invalidOIDCProviderConfig.Namespace, + invalidOIDCProviderConfig, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + validOIDCProviderConfig.Namespace, + validOIDCProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + validOIDCProviderConfig.Namespace, + validOIDCProviderConfig, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + + when("updating only the invalid OIDCProviderConfig fails for a reason other than conflict", func() { + it.Before(func() { + pinnipedAPIClient.PrependReactor( + "update", + "oidcproviderconfigs", + func(action coretesting.Action) (bool, runtime.Object, error) { + updateAction := action.(coretesting.UpdateActionImpl) + opc := updateAction.Object.(*v1alpha1.OIDCProviderConfig) + if opc.Name == validOIDCProviderConfig.Name { + return true, nil, nil + } + + return true, nil, errors.New("some update error") + }, + ) + }) + + it("sets the provider that it could actually update in the API", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, "1 error(s):\n- could not update status: some update error") + + validProvider, err := provider.NewOIDCProvider(validOIDCProviderConfig.Spec.Issuer) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.Equal( + []*provider.OIDCProvider{ + validProvider, + }, + providersSetter.OIDCProvidersReceived, + ) + }) + + it("returns an error", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, "1 error(s):\n- could not update status: some update error") + + validOIDCProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + validOIDCProviderConfig.Status.Message = "Provider successfully created" + + invalidOIDCProviderConfig.Status.Status = v1alpha1.InvalidOIDCProviderStatus + invalidOIDCProviderConfig.Status.Message = "Invalid: issuer must not have query" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + invalidOIDCProviderConfig.Namespace, + invalidOIDCProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + invalidOIDCProviderConfig.Namespace, + invalidOIDCProviderConfig, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + validOIDCProviderConfig.Namespace, + validOIDCProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + validOIDCProviderConfig.Namespace, + validOIDCProviderConfig, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + }) }) - when("they there are OIDCProviderConfigs with duplicate issuer names in the informer", func() { - // TODO write this test + when("there are OIDCProviderConfigs with duplicate issuer names in the informer", func() { + var ( + oidcProviderConfigDuplicate1 *v1alpha1.OIDCProviderConfig + oidcProviderConfigDuplicate2 *v1alpha1.OIDCProviderConfig + oidcProviderConfig *v1alpha1.OIDCProviderConfig + ) + + it.Before(func() { + oidcProviderConfigDuplicate1 = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "duplicate1", Namespace: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer-duplicate.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfigDuplicate1)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfigDuplicate1)) + oidcProviderConfigDuplicate2 = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "duplicate2", Namespace: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer-duplicate.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfigDuplicate2)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfigDuplicate2)) + + oidcProviderConfig = &v1alpha1.OIDCProviderConfig{ + ObjectMeta: metav1.ObjectMeta{Name: "not-duplicate", Namespace: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer-not-duplicate.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig)) + }) + + it("calls the ProvidersSetter with the non-duplicate", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + nonDuplicateProvider, err := provider.NewOIDCProvider(oidcProviderConfig.Spec.Issuer) + r.NoError(err) + + r.True(providersSetter.SetProvidersWasCalled) + r.Equal( + []*provider.OIDCProvider{ + nonDuplicateProvider, + }, + providersSetter.OIDCProvidersReceived, + ) + }) + + it("updates the statuses", func() { + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.NoError(err) + + oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig.Status.Message = "Provider successfully created" + + oidcProviderConfigDuplicate1.Status.Status = v1alpha1.DuplicateOIDCProviderStatus + oidcProviderConfigDuplicate1.Status.Message = "Duplicate issuer: https://issuer-duplicate.com" + + oidcProviderConfigDuplicate2.Status.Status = v1alpha1.DuplicateOIDCProviderStatus + oidcProviderConfigDuplicate2.Status.Message = "Duplicate issuer: https://issuer-duplicate.com" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfigDuplicate1.Namespace, + oidcProviderConfigDuplicate1.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfigDuplicate1.Namespace, + oidcProviderConfigDuplicate1, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfigDuplicate2.Namespace, + oidcProviderConfigDuplicate2.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfigDuplicate2.Namespace, + oidcProviderConfigDuplicate2, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig.Name, + ), + coretesting.NewUpdateAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + + when("we cannot talk to the API", func() { + it.Before(func() { + pinnipedAPIClient.PrependReactor( + "get", + "oidcproviderconfigs", + func(_ coretesting.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("some get error") + }, + ) + }) + + it("returns the get errors", func() { + expectedError := here.Doc(` + 3 error(s): + - could not update status: get failed: some get error + - could not update status: get failed: some get error + - could not update status: get failed: some get error`) + startInformersAndController() + err := controllerlib.TestSync(t, subject, *syncContext) + r.EqualError(err, expectedError) + + oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus + oidcProviderConfig.Status.Message = "Provider successfully created" + + expectedActions := []coretesting.Action{ + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfigDuplicate1.Namespace, + oidcProviderConfigDuplicate1.Name, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfigDuplicate2.Namespace, + oidcProviderConfigDuplicate2.Name, + ), + coretesting.NewGetAction( + oidcProviderConfigGVR, + oidcProviderConfig.Namespace, + oidcProviderConfig.Name, + ), + } + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) + }) + }) }) when("there are no OIDCProviderConfigs in the informer", func() { diff --git a/internal/multierror/multierror.go b/internal/multierror/multierror.go index c9eab6b98..5f87107ff 100644 --- a/internal/multierror/multierror.go +++ b/internal/multierror/multierror.go @@ -17,6 +17,14 @@ import ( "strings" ) +// formatFunc is a function used to format the string representing of a MultiError. It is used in the +// Error() function. +// +// It is marked out here to indicate how we could potentially extend MultiError in the future to +// support more styles of converting from a list of error's to a string. +//nolint: gochecknoglobals +var formatFunc func(errs MultiError, sb *strings.Builder) = defaultFormat + // MultiError holds a list of error's, that could potentially be empty. // // Use New() to create a MultiError. @@ -35,10 +43,7 @@ func (m *MultiError) Add(err error) { // Error implements the error.Error() interface method. func (m MultiError) Error() string { sb := strings.Builder{} - _, _ = fmt.Fprintf(&sb, "%d error(s):", len(m)) - for _, err := range m { - _, _ = fmt.Fprintf(&sb, "\n- %s", err.Error()) - } + formatFunc(m, &sb) return sb.String() } @@ -49,3 +54,10 @@ func (m MultiError) ErrOrNil() error { } return nil } + +func defaultFormat(errs MultiError, sb *strings.Builder) { + _, _ = fmt.Fprintf(sb, "%d error(s):", len(errs)) + for _, err := range errs { + _, _ = fmt.Fprintf(sb, "\n- %s", err.Error()) + } +} From c555c14ccb05c0b7357e025185e8292d55872bcf Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Fri, 9 Oct 2020 11:54:50 -0400 Subject: [PATCH 21/28] supervisor-oidc: add OIDCProviderConfig.Status.LastUpdateTime Signed-off-by: Andrew Keesler --- .../v1alpha1/types_oidcproviderconfig.go.tmpl | 6 ++++++ ...config.pinniped.dev_oidcproviderconfigs.yaml | 8 +++++++- generated/1.17/README.adoc | 3 ++- .../config/v1alpha1/types_oidcproviderconfig.go | 8 +++++++- .../config/v1alpha1/zz_generated.deepcopy.go | 6 +++++- .../1.17/client/openapi/zz_generated.openapi.go | 10 +++++++++- ...config.pinniped.dev_oidcproviderconfigs.yaml | 8 +++++++- generated/1.18/README.adoc | 3 ++- .../config/v1alpha1/types_oidcproviderconfig.go | 8 +++++++- .../config/v1alpha1/zz_generated.deepcopy.go | 6 +++++- .../1.18/client/openapi/zz_generated.openapi.go | 10 +++++++++- ...config.pinniped.dev_oidcproviderconfigs.yaml | 8 +++++++- generated/1.19/README.adoc | 3 ++- .../config/v1alpha1/types_oidcproviderconfig.go | 8 +++++++- .../config/v1alpha1/zz_generated.deepcopy.go | 6 +++++- .../1.19/client/openapi/zz_generated.openapi.go | 10 +++++++++- ...config.pinniped.dev_oidcproviderconfigs.yaml | 8 +++++++- .../oidcproviderconfig_watcher.go | 3 +++ .../oidcproviderconfig_watcher_test.go | 17 +++++++++++++++++ 19 files changed, 123 insertions(+), 16 deletions(-) diff --git a/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl index a2850bdf8..a05f4339d 100644 --- a/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl +++ b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl @@ -38,6 +38,12 @@ type OIDCProviderConfigStatus struct { // Message provides human-readable details about the Status. // +optional Message string `json:"message,omitempty"` + + // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get + // around some undesirable behavior with respect to the empty metav1.Time value (see + // https://github.com/kubernetes/kubernetes/issues/86811). + // +optional + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` } // OIDCProviderConfig describes the configuration of an OIDC provider. diff --git a/deploy/config.pinniped.dev_oidcproviderconfigs.yaml b/deploy/config.pinniped.dev_oidcproviderconfigs.yaml index c1cb55256..062dca9f5 100644 --- a/deploy/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/deploy/config.pinniped.dev_oidcproviderconfigs.yaml @@ -55,12 +55,18 @@ spec: status: description: Status of the OIDC provider. properties: + lastUpdateTime: + description: LastUpdateTime holds the time at which the Status was + last updated. It is a pointer to get around some undesirable behavior + with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). + format: date-time + type: string message: description: Message provides human-readable details about the Status. type: string status: description: Status holds an enum that describes the state of this - OIDCProvider. Note that this Status can represent success or failure. + OIDC Provider. Note that this Status can represent success or failure. enum: - Success - Duplicate diff --git a/generated/1.17/README.adoc b/generated/1.17/README.adoc index 716d1b036..c74ee7435 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -148,8 +148,9 @@ OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure. +| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. | *`message`* __string__ | Message provides human-readable details about the Status. +| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). |=== diff --git a/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go index 75931e70b..a05f4339d 100644 --- a/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go +++ b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -30,7 +30,7 @@ type OIDCProviderConfigSpec struct { // OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. type OIDCProviderConfigStatus struct { - // Status holds an enum that describes the state of this OIDCProvider. Note that this Status can + // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can // represent success or failure. // +optional Status OIDCProviderStatus `json:"status,omitempty"` @@ -38,6 +38,12 @@ type OIDCProviderConfigStatus struct { // Message provides human-readable details about the Status. // +optional Message string `json:"message,omitempty"` + + // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get + // around some undesirable behavior with respect to the empty metav1.Time value (see + // https://github.com/kubernetes/kubernetes/issues/86811). + // +optional + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` } // OIDCProviderConfig describes the configuration of an OIDC provider. diff --git a/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go index 253a5c5ba..262992cbc 100644 --- a/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.17/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -138,7 +138,7 @@ func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } @@ -212,6 +212,10 @@ func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCProviderConfigStatus) DeepCopyInto(out *OIDCProviderConfigStatus) { *out = *in + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } return } diff --git a/generated/1.17/client/openapi/zz_generated.openapi.go b/generated/1.17/client/openapi/zz_generated.openapi.go index d25d0c88e..8e5ad5b89 100644 --- a/generated/1.17/client/openapi/zz_generated.openapi.go +++ b/generated/1.17/client/openapi/zz_generated.openapi.go @@ -413,7 +413,7 @@ func schema_117_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.Referen Properties: map[string]spec.Schema{ "status": { SchemaProps: spec.SchemaProps{ - Description: "Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure.", + Description: "Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure.", Type: []string{"string"}, Format: "", }, @@ -425,9 +425,17 @@ func schema_117_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.Referen Format: "", }, }, + "lastUpdateTime": { + SchemaProps: spec.SchemaProps{ + Description: "LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811).", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, }, }, }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml index c1cb55256..062dca9f5 100644 --- a/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -55,12 +55,18 @@ spec: status: description: Status of the OIDC provider. properties: + lastUpdateTime: + description: LastUpdateTime holds the time at which the Status was + last updated. It is a pointer to get around some undesirable behavior + with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). + format: date-time + type: string message: description: Message provides human-readable details about the Status. type: string status: description: Status holds an enum that describes the state of this - OIDCProvider. Note that this Status can represent success or failure. + OIDC Provider. Note that this Status can represent success or failure. enum: - Success - Duplicate diff --git a/generated/1.18/README.adoc b/generated/1.18/README.adoc index 6eef02467..eaa1e5bf2 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -148,8 +148,9 @@ OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure. +| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. | *`message`* __string__ | Message provides human-readable details about the Status. +| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). |=== diff --git a/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go index 75931e70b..a05f4339d 100644 --- a/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go +++ b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -30,7 +30,7 @@ type OIDCProviderConfigSpec struct { // OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. type OIDCProviderConfigStatus struct { - // Status holds an enum that describes the state of this OIDCProvider. Note that this Status can + // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can // represent success or failure. // +optional Status OIDCProviderStatus `json:"status,omitempty"` @@ -38,6 +38,12 @@ type OIDCProviderConfigStatus struct { // Message provides human-readable details about the Status. // +optional Message string `json:"message,omitempty"` + + // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get + // around some undesirable behavior with respect to the empty metav1.Time value (see + // https://github.com/kubernetes/kubernetes/issues/86811). + // +optional + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` } // OIDCProviderConfig describes the configuration of an OIDC provider. diff --git a/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go index 253a5c5ba..262992cbc 100644 --- a/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.18/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -138,7 +138,7 @@ func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } @@ -212,6 +212,10 @@ func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCProviderConfigStatus) DeepCopyInto(out *OIDCProviderConfigStatus) { *out = *in + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } return } diff --git a/generated/1.18/client/openapi/zz_generated.openapi.go b/generated/1.18/client/openapi/zz_generated.openapi.go index eb0e2dfa0..75393f9a8 100644 --- a/generated/1.18/client/openapi/zz_generated.openapi.go +++ b/generated/1.18/client/openapi/zz_generated.openapi.go @@ -413,7 +413,7 @@ func schema_118_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.Referen Properties: map[string]spec.Schema{ "status": { SchemaProps: spec.SchemaProps{ - Description: "Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure.", + Description: "Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure.", Type: []string{"string"}, Format: "", }, @@ -425,9 +425,17 @@ func schema_118_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.Referen Format: "", }, }, + "lastUpdateTime": { + SchemaProps: spec.SchemaProps{ + Description: "LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811).", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, }, }, }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml index c1cb55256..062dca9f5 100644 --- a/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -55,12 +55,18 @@ spec: status: description: Status of the OIDC provider. properties: + lastUpdateTime: + description: LastUpdateTime holds the time at which the Status was + last updated. It is a pointer to get around some undesirable behavior + with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). + format: date-time + type: string message: description: Message provides human-readable details about the Status. type: string status: description: Status holds an enum that describes the state of this - OIDCProvider. Note that this Status can represent success or failure. + OIDC Provider. Note that this Status can represent success or failure. enum: - Success - Duplicate diff --git a/generated/1.19/README.adoc b/generated/1.19/README.adoc index 3008fb692..1dc10f4c7 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -148,8 +148,9 @@ OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure. +| *`status`* __OIDCProviderStatus__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. | *`message`* __string__ | Message provides human-readable details about the Status. +| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). |=== diff --git a/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go index 75931e70b..a05f4339d 100644 --- a/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go +++ b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -30,7 +30,7 @@ type OIDCProviderConfigSpec struct { // OIDCProviderConfigStatus is a struct that describes the actual state of an OIDC Provider. type OIDCProviderConfigStatus struct { - // Status holds an enum that describes the state of this OIDCProvider. Note that this Status can + // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can // represent success or failure. // +optional Status OIDCProviderStatus `json:"status,omitempty"` @@ -38,6 +38,12 @@ type OIDCProviderConfigStatus struct { // Message provides human-readable details about the Status. // +optional Message string `json:"message,omitempty"` + + // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get + // around some undesirable behavior with respect to the empty metav1.Time value (see + // https://github.com/kubernetes/kubernetes/issues/86811). + // +optional + LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` } // OIDCProviderConfig describes the configuration of an OIDC provider. diff --git a/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go index 253a5c5ba..262992cbc 100644 --- a/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.19/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -138,7 +138,7 @@ func (in *OIDCProviderConfig) DeepCopyInto(out *OIDCProviderConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) return } @@ -212,6 +212,10 @@ func (in *OIDCProviderConfigSpec) DeepCopy() *OIDCProviderConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCProviderConfigStatus) DeepCopyInto(out *OIDCProviderConfigStatus) { *out = *in + if in.LastUpdateTime != nil { + in, out := &in.LastUpdateTime, &out.LastUpdateTime + *out = (*in).DeepCopy() + } return } diff --git a/generated/1.19/client/openapi/zz_generated.openapi.go b/generated/1.19/client/openapi/zz_generated.openapi.go index c7ed50845..277a97cbe 100644 --- a/generated/1.19/client/openapi/zz_generated.openapi.go +++ b/generated/1.19/client/openapi/zz_generated.openapi.go @@ -414,7 +414,7 @@ func schema_119_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.Referen Properties: map[string]spec.Schema{ "status": { SchemaProps: spec.SchemaProps{ - Description: "Status holds an enum that describes the state of this OIDCProvider. Note that this Status can represent success or failure.", + Description: "Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure.", Type: []string{"string"}, Format: "", }, @@ -426,9 +426,17 @@ func schema_119_apis_config_v1alpha1_OIDCProviderConfigStatus(ref common.Referen Format: "", }, }, + "lastUpdateTime": { + SchemaProps: spec.SchemaProps{ + Description: "LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811).", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, }, }, }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml index c1cb55256..062dca9f5 100644 --- a/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml +++ b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -55,12 +55,18 @@ spec: status: description: Status of the OIDC provider. properties: + lastUpdateTime: + description: LastUpdateTime holds the time at which the Status was + last updated. It is a pointer to get around some undesirable behavior + with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). + format: date-time + type: string message: description: Message provides human-readable details about the Status. type: string status: description: Status holds an enum that describes the state of this - OIDCProvider. Note that this Status can represent success or failure. + OIDC Provider. Note that this Status can represent success or failure. enum: - Success - Duplicate diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go index 3f21d5fe6..d6420f4c8 100644 --- a/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go @@ -152,7 +152,10 @@ func (c *oidcProviderConfigWatcherController) updateStatus( ) opc.Status.Status = status opc.Status.Message = message + opc.Status.LastUpdateTime = timePtr(metav1.NewTime(c.clock.Now())) _, err = c.client.ConfigV1alpha1().OIDCProviderConfigs(namespace).Update(ctx, opc, metav1.UpdateOptions{}) return err }) } + +func timePtr(t metav1.Time) *metav1.Time { return &t } diff --git a/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go index a7f549a86..7db8022d0 100644 --- a/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go @@ -208,9 +208,11 @@ func TestSync(t *testing.T) { oidcProviderConfig1.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig1.Status.Message = "Provider successfully created" + oidcProviderConfig1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) oidcProviderConfig2.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig2.Status.Message = "Provider successfully created" + oidcProviderConfig2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -241,6 +243,7 @@ func TestSync(t *testing.T) { it.Before(func() { oidcProviderConfig1.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig1.Status.Message = "Provider successfully created" + oidcProviderConfig1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) r.NoError(pinnipedAPIClient.Tracker().Update(oidcProviderConfigGVR, oidcProviderConfig1, oidcProviderConfig1.Namespace)) r.NoError(opcInformerClient.Tracker().Update(oidcProviderConfigGVR, oidcProviderConfig1, oidcProviderConfig1.Namespace)) @@ -253,6 +256,7 @@ func TestSync(t *testing.T) { oidcProviderConfig2.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig2.Status.Message = "Provider successfully created" + oidcProviderConfig2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -338,9 +342,11 @@ func TestSync(t *testing.T) { oidcProviderConfig1.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig1.Status.Message = "Provider successfully created" + oidcProviderConfig1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) oidcProviderConfig2.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig2.Status.Message = "Provider successfully created" + oidcProviderConfig2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -406,6 +412,7 @@ func TestSync(t *testing.T) { oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig.Status.Message = "Provider successfully created" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -451,6 +458,7 @@ func TestSync(t *testing.T) { oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig.Status.Message = "Provider successfully created" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -486,6 +494,7 @@ func TestSync(t *testing.T) { oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig.Status.Message = "Provider successfully created" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -545,9 +554,11 @@ func TestSync(t *testing.T) { validOIDCProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus validOIDCProviderConfig.Status.Message = "Provider successfully created" + validOIDCProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) invalidOIDCProviderConfig.Status.Status = v1alpha1.InvalidOIDCProviderStatus invalidOIDCProviderConfig.Status.Message = "Invalid: issuer must not have query" + invalidOIDCProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -615,9 +626,11 @@ func TestSync(t *testing.T) { validOIDCProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus validOIDCProviderConfig.Status.Message = "Provider successfully created" + validOIDCProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) invalidOIDCProviderConfig.Status.Status = v1alpha1.InvalidOIDCProviderStatus invalidOIDCProviderConfig.Status.Message = "Invalid: issuer must not have query" + invalidOIDCProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -699,12 +712,15 @@ func TestSync(t *testing.T) { oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig.Status.Message = "Provider successfully created" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) oidcProviderConfigDuplicate1.Status.Status = v1alpha1.DuplicateOIDCProviderStatus oidcProviderConfigDuplicate1.Status.Message = "Duplicate issuer: https://issuer-duplicate.com" + oidcProviderConfigDuplicate1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) oidcProviderConfigDuplicate2.Status.Status = v1alpha1.DuplicateOIDCProviderStatus oidcProviderConfigDuplicate2.Status.Message = "Duplicate issuer: https://issuer-duplicate.com" + oidcProviderConfigDuplicate2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( @@ -764,6 +780,7 @@ func TestSync(t *testing.T) { oidcProviderConfig.Status.Status = v1alpha1.SuccessOIDCProviderStatus oidcProviderConfig.Status.Message = "Provider successfully created" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) expectedActions := []coretesting.Action{ coretesting.NewGetAction( From f5a6a0bb1edca5ea881b9a9033037b8ad3702d14 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 10:00:22 -0700 Subject: [PATCH 22/28] Move all three deployment dirs under a new top-level `deploy/` dir --- deploy/{ => concierge}/README.md | 8 ++++---- ...fig.pinniped.dev_credentialissuerconfigs.yaml | 0 deploy/{ => concierge}/deployment.yaml | 0 ...dp.pinniped.dev_webhookidentityproviders.yaml | 0 deploy/{ => concierge}/rbac.yaml | 0 deploy/{ => concierge}/values.yaml | 0 .../local-user-authenticator}/README.md | 8 ++++---- .../local-user-authenticator}/deployment.yaml | 0 .../local-user-authenticator}/rbac.yaml | 0 .../local-user-authenticator}/values.yaml | 0 .../supervisor}/README.md | 6 +++--- .../config.pinniped.dev_oidcproviderconfigs.yaml | 0 .../supervisor}/deployment.yaml | 0 .../supervisor}/rbac.yaml | 0 .../supervisor}/values.yaml | 0 doc/demo.md | 12 ++++++------ hack/lib/tilt/Tiltfile | 4 ++-- hack/prepare-for-integration-tests.sh | 6 +++--- hack/update.sh | 16 +++++++++++++++- 19 files changed, 37 insertions(+), 23 deletions(-) rename deploy/{ => concierge}/README.md (86%) rename deploy/{ => concierge}/config.pinniped.dev_credentialissuerconfigs.yaml (100%) rename deploy/{ => concierge}/deployment.yaml (100%) rename deploy/{ => concierge}/idp.pinniped.dev_webhookidentityproviders.yaml (100%) rename deploy/{ => concierge}/rbac.yaml (100%) rename deploy/{ => concierge}/values.yaml (100%) rename {deploy-local-user-authenticator => deploy/local-user-authenticator}/README.md (96%) rename {deploy-local-user-authenticator => deploy/local-user-authenticator}/deployment.yaml (100%) rename {deploy-local-user-authenticator => deploy/local-user-authenticator}/rbac.yaml (100%) rename {deploy-local-user-authenticator => deploy/local-user-authenticator}/values.yaml (100%) rename {deploy-supervisor => deploy/supervisor}/README.md (90%) rename deploy/{ => supervisor}/config.pinniped.dev_oidcproviderconfigs.yaml (100%) rename {deploy-supervisor => deploy/supervisor}/deployment.yaml (100%) rename {deploy-supervisor => deploy/supervisor}/rbac.yaml (100%) rename {deploy-supervisor => deploy/supervisor}/values.yaml (100%) diff --git a/deploy/README.md b/deploy/concierge/README.md similarity index 86% rename from deploy/README.md rename to deploy/concierge/README.md index 9f5cb13d3..aecbeb0e6 100644 --- a/deploy/README.md +++ b/deploy/concierge/README.md @@ -4,7 +4,7 @@ If you would like to try Pinniped, but you don't have a compatible identity provider, you can use Pinniped's test identity provider. -See [deploy-local-user-authenticator/README.md](../deploy-local-user-authenticator/README.md) +See [deploy/local-user-authenticator/README.md](../../deploy/local-user-authenticator/README.md) for details. ## Installing the Latest Version with Default Options @@ -26,14 +26,14 @@ kubectl apply -f https://github.com/vmware-tanzu/pinniped/releases/download/v0.2 ## Installing with Custom Options Creating your own deployment YAML file requires `ytt` from [Carvel](https://carvel.dev/) to template the YAML files -in the [deploy](../deploy) directory. +in the `deploy/concierge` directory. Either [install `ytt`](https://get-ytt.io/) or use the [container image from Dockerhub](https://hub.docker.com/r/k14s/image/tags). 1. `git clone` this repo and `git checkout` the release version tag of the release that you would like to deploy. -1. The configuration options are in [deploy/values.yml](values.yaml). +1. The configuration options are in [deploy/concierge/values.yml](values.yaml). Fill in the values in that file, or override those values using additional `ytt` command-line options in the command below. Use the release version tag as the `image_tag` value. -2. In a terminal, cd to this `deploy` directory +2. In a terminal, cd to this `deploy/concierge` directory 3. To generate the final YAML files, run `ytt --file .` 4. Deploy the generated YAML using your preferred deployment tool, such as `kubectl` or [`kapp`](https://get-kapp.io/). For example: `ytt --file . | kapp deploy --yes --app pinniped --diff-changes --file -` diff --git a/deploy/config.pinniped.dev_credentialissuerconfigs.yaml b/deploy/concierge/config.pinniped.dev_credentialissuerconfigs.yaml similarity index 100% rename from deploy/config.pinniped.dev_credentialissuerconfigs.yaml rename to deploy/concierge/config.pinniped.dev_credentialissuerconfigs.yaml diff --git a/deploy/deployment.yaml b/deploy/concierge/deployment.yaml similarity index 100% rename from deploy/deployment.yaml rename to deploy/concierge/deployment.yaml diff --git a/deploy/idp.pinniped.dev_webhookidentityproviders.yaml b/deploy/concierge/idp.pinniped.dev_webhookidentityproviders.yaml similarity index 100% rename from deploy/idp.pinniped.dev_webhookidentityproviders.yaml rename to deploy/concierge/idp.pinniped.dev_webhookidentityproviders.yaml diff --git a/deploy/rbac.yaml b/deploy/concierge/rbac.yaml similarity index 100% rename from deploy/rbac.yaml rename to deploy/concierge/rbac.yaml diff --git a/deploy/values.yaml b/deploy/concierge/values.yaml similarity index 100% rename from deploy/values.yaml rename to deploy/concierge/values.yaml diff --git a/deploy-local-user-authenticator/README.md b/deploy/local-user-authenticator/README.md similarity index 96% rename from deploy-local-user-authenticator/README.md rename to deploy/local-user-authenticator/README.md index d1d2f4044..44334af90 100644 --- a/deploy-local-user-authenticator/README.md +++ b/deploy/local-user-authenticator/README.md @@ -31,14 +31,14 @@ kubectl apply -f https://github.com/vmware-tanzu/pinniped/releases/download/v0.2 ## Installing with Custom Options Creating your own deployment YAML file requires `ytt` from [Carvel](https://carvel.dev/) to template the YAML files -in the [deploy-local-user-authenticator](../deploy-local-user-authenticator) directory. +in the `deploy/local-user-authenticator` directory. Either [install `ytt`](https://get-ytt.io/) or use the [container image from Dockerhub](https://hub.docker.com/r/k14s/image/tags). 1. `git clone` this repo and `git checkout` the release version tag of the release that you would like to deploy. -1. The configuration options are in [deploy-local-user-authenticator/values.yml](values.yaml). +1. The configuration options are in [deploy/local-user-authenticator/values.yml](values.yaml). Fill in the values in that file, or override those values using additional `ytt` command-line options in the command below. Use the release version tag as the `image_tag` value. -2. In a terminal, cd to this `deploy-local-user-authenticator` directory +2. In a terminal, cd to this `deploy/local-user-authenticator` directory 3. To generate the final YAML files, run `ytt --file .` 4. Deploy the generated YAML using your preferred deployment tool, such as `kubectl` or [`kapp`](https://get-kapp.io/). For example: `ytt --file . | kapp deploy --yes --app local-user-authenticator --diff-changes --file -` @@ -79,7 +79,7 @@ kubectl get secret local-user-authenticator-tls-serving-certificate --namespace When installing Pinniped on the same cluster, configure local-user-authenticator as an Identity Provider for Pinniped using the webhook URL `https://local-user-authenticator.local-user-authenticator.svc/authenticate` -along with the CA bundle fetched by the above command. See [doc/demo.md](../doc/demo.md) for an example. +along with the CA bundle fetched by the above command. See [doc/demo.md](../../doc/demo.md) for an example. ## Optional: Manually Testing the Webhook Endpoint After Installing diff --git a/deploy-local-user-authenticator/deployment.yaml b/deploy/local-user-authenticator/deployment.yaml similarity index 100% rename from deploy-local-user-authenticator/deployment.yaml rename to deploy/local-user-authenticator/deployment.yaml diff --git a/deploy-local-user-authenticator/rbac.yaml b/deploy/local-user-authenticator/rbac.yaml similarity index 100% rename from deploy-local-user-authenticator/rbac.yaml rename to deploy/local-user-authenticator/rbac.yaml diff --git a/deploy-local-user-authenticator/values.yaml b/deploy/local-user-authenticator/values.yaml similarity index 100% rename from deploy-local-user-authenticator/values.yaml rename to deploy/local-user-authenticator/values.yaml diff --git a/deploy-supervisor/README.md b/deploy/supervisor/README.md similarity index 90% rename from deploy-supervisor/README.md rename to deploy/supervisor/README.md index 0a1c31061..7920b6cbd 100644 --- a/deploy-supervisor/README.md +++ b/deploy/supervisor/README.md @@ -24,14 +24,14 @@ kubectl apply -f https://github.com/vmware-tanzu/pinniped/releases/download/v0.3 ## Installing with Custom Options Creating your own deployment YAML file requires `ytt` from [Carvel](https://carvel.dev/) to template the YAML files -in the [deploy-supervisor](../deploy-supervisor) directory. +in the `deploy/supervisor` directory. Either [install `ytt`](https://get-ytt.io/) or use the [container image from Dockerhub](https://hub.docker.com/r/k14s/image/tags). 1. `git clone` this repo and `git checkout` the release version tag of the release that you would like to deploy. -1. The configuration options are in [deploy-supervisor/values.yml](values.yaml). +1. The configuration options are in [deploy/supervisor/values.yml](values.yaml). Fill in the values in that file, or override those values using additional `ytt` command-line options in the command below. Use the release version tag as the `image_tag` value. -2. In a terminal, cd to this `deploy-supervisor` directory +2. In a terminal, cd to this `deploy/supervisor` directory 3. To generate the final YAML files, run `ytt --file .` 4. Deploy the generated YAML using your preferred deployment tool, such as `kubectl` or [`kapp`](https://get-kapp.io/). For example: `ytt --file . | kapp deploy --yes --app pinniped-supervisor --diff-changes --file -` diff --git a/deploy/config.pinniped.dev_oidcproviderconfigs.yaml b/deploy/supervisor/config.pinniped.dev_oidcproviderconfigs.yaml similarity index 100% rename from deploy/config.pinniped.dev_oidcproviderconfigs.yaml rename to deploy/supervisor/config.pinniped.dev_oidcproviderconfigs.yaml diff --git a/deploy-supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml similarity index 100% rename from deploy-supervisor/deployment.yaml rename to deploy/supervisor/deployment.yaml diff --git a/deploy-supervisor/rbac.yaml b/deploy/supervisor/rbac.yaml similarity index 100% rename from deploy-supervisor/rbac.yaml rename to deploy/supervisor/rbac.yaml diff --git a/deploy-supervisor/values.yaml b/deploy/supervisor/values.yaml similarity index 100% rename from deploy-supervisor/values.yaml rename to deploy/supervisor/values.yaml diff --git a/doc/demo.md b/doc/demo.md index 52d9e7662..6318988cd 100644 --- a/doc/demo.md +++ b/doc/demo.md @@ -11,7 +11,7 @@ Don't have an identity provider of a type supported by Pinniped handy? No problem, there is a demo identity provider available. Start by installing local-user-authenticator on the same cluster where you would like to try Pinniped - by following the directions in [deploy-local-user-authenticator/README.md](../deploy-local-user-authenticator/README.md). + by following the directions in [deploy/local-user-authenticator/README.md](../deploy/local-user-authenticator/README.md). See below for an example of deploying this on kind. 1. A kubeconfig where the current context points to the cluster and has admin-like @@ -22,7 +22,7 @@ Installing and trying Pinniped on any cluster will consist of the following general steps. See the next section below for a more specific example of installing onto a local kind cluster, including the exact commands to use for that case. -1. Install Pinniped. See [deploy/README.md](../deploy/README.md). +1. Install Pinniped. See [deploy/concierge/README.md](../deploy/concierge/README.md). 1. Download the Pinniped CLI from [Pinniped's github Releases page](https://github.com/vmware-tanzu/pinniped/releases/latest). 1. Generate a kubeconfig using the Pinniped CLI. Run `pinniped get-kubeconfig --help` for more information. 1. Run `kubectl` commands using the generated kubeconfig. Pinniped will automatically be used for authentication during those commands. @@ -38,9 +38,9 @@ as the identity provider.

-Pinniped Installation Demo

@@ -88,7 +88,7 @@ as the identity provider. The `install-local-user-authenticator.yaml` file includes the default deployment options. If you would prefer to customize the available options, please - see [deploy-local-user-authenticator/README.md](../deploy-local-user-authenticator/README.md) + see [deploy/local-user-authenticator/README.md](../deploy/local-user-authenticator/README.md) for instructions on how to deploy using `ytt`. 1. Create a test user named `pinny-the-seal` in the local-user-authenticator identity provider. @@ -115,7 +115,7 @@ as the identity provider. ``` The `install-pinniped.yaml` file includes the default deployment options. - If you would prefer to customize the available options, please see [deploy/README.md](../deploy/README.md) + If you would prefer to customize the available options, please see [deploy/concierge/README.md](../deploy/concierge/README.md) for instructions on how to deploy using `ytt`. 1. Create a `WebhookIdentityProvider` object to configure Pinniped to authenticate using local-user-authenticator. diff --git a/hack/lib/tilt/Tiltfile b/hack/lib/tilt/Tiltfile index 0a7372efc..16b770772 100644 --- a/hack/lib/tilt/Tiltfile +++ b/hack/lib/tilt/Tiltfile @@ -26,7 +26,7 @@ docker_build_with_restart('image/local-user-auth', '.', # Render the local-user-authenticator installation manifest using ytt. k8s_yaml(local([ 'ytt', - '--file', '../../../deploy-local-user-authenticator', + '--file', '../../../deploy/local-user-authenticator', '--data-value', 'image_repo=image/local-user-auth', '--data-value', 'image_tag=tilt-dev', ])) @@ -54,7 +54,7 @@ docker_build_with_restart('image/pinniped', '.', # Render the Pinniped server installation manifest using ytt. k8s_yaml(local([ 'sh', '-c', - 'ytt --file ../../../deploy ' + + 'ytt --file ../../../deploy/concierge ' + '--data-value namespace=integration ' + '--data-value image_repo=image/pinniped ' + '--data-value image_tag=tilt-dev ' + diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index 7985140a8..0e2cd87f9 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -164,7 +164,7 @@ if ! tilt_mode; then # # Deploy local-user-authenticator # - pushd deploy-local-user-authenticator >/dev/null + pushd deploy/local-user-authenticator >/dev/null log_note "Deploying the local-user-authenticator app to the cluster..." ytt --file . \ @@ -203,7 +203,7 @@ kubectl create secret generic "$test_username" \ supervisor_app_name="pinniped-supervisor" supervisor_namespace="pinniped-supervisor" -pushd deploy-supervisor >/dev/null +pushd deploy/supervisor >/dev/null log_note "Deploying the Pinniped Supervisor app to the cluster..." ytt --file . \ @@ -246,7 +246,7 @@ if ! tilt_mode; then # # Deploy Pinniped # - pushd deploy >/dev/null + pushd deploy/concierge >/dev/null log_note "Deploying the Pinniped app to the cluster..." ytt --file . \ diff --git a/hack/update.sh b/hack/update.sh index e6df6b944..98593f03a 100755 --- a/hack/update.sh +++ b/hack/update.sh @@ -7,6 +7,20 @@ set -euo pipefail ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" +# Generate code. xargs "$ROOT/hack/lib/update-codegen.sh" < "${ROOT}/hack/lib/kube-versions.txt" -cp "$ROOT/generated/1.19/crds/"*.yaml "$ROOT/deploy/" + +# Copy each CRD yaml to the app which should cause it to be installed. +cp "$ROOT"/generated/1.19/crds/*oidcproviderconfigs.yaml "$ROOT/deploy/supervisor" +cp "$ROOT"/generated/1.19/crds/*credentialissuerconfigs.yaml "$ROOT/deploy/concierge" +cp "$ROOT"/generated/1.19/crds/*webhookidentityproviders.yaml "$ROOT/deploy/concierge" + +# Make sure we didn't miss any new CRDs. +crdCount=$(find "$ROOT"/generated/1.19/crds/ -maxdepth 1 -type f -name '*.yaml' | wc -l | tr -d ' ') +if [[ "$crdCount" != "3" ]]; then + echo "Looks like you added a new CRD. Please update this update.sh script to decide where to copy it and then run it again." + exit 1 +fi + +# Tidy. "$ROOT/hack/module.sh" tidy From 72b2d027772f0cf55940e08ec4ab2f085c909253 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 10:11:47 -0700 Subject: [PATCH 23/28] Rename integration test env variables - Variables specific to concierge add it to their name - All variables now start with `PINNIPED_TEST_` which makes it clear that they are for tests and also helps them not conflict with the env vars that are used in the Pinniped CLI code --- hack/prepare-for-integration-tests.sh | 16 +++++----- test/integration/cli_test.go | 31 ++----------------- test/integration/client_test.go | 2 +- .../concierge_api_serving_certs_test.go | 8 ++--- .../concierge_availability_test.go | 2 +- .../concierge_credentialissuerconfig_test.go | 2 +- .../concierge_credentialrequest_test.go | 4 +-- .../concierge_kubecertagent_test.go | 8 ++--- test/library/client.go | 4 +-- test/library/env.go | 21 +++++++------ 10 files changed, 37 insertions(+), 61 deletions(-) diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index 0e2cd87f9..f792ce24b 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -269,22 +269,22 @@ pinniped_cluster_capability_file_content=$(cat "$kind_capabilities_file") cat </tmp/integration-test-env # The following env vars should be set before running 'go test -v -count 1 ./test/...' -export PINNIPED_NAMESPACE=${namespace} -export PINNIPED_APP_NAME=${app_name} +export PINNIPED_TEST_CONCIERGE_NAMESPACE=${namespace} +export PINNIPED_TEST_CONCIERGE_APP_NAME=${app_name} export PINNIPED_TEST_USER_USERNAME=${test_username} export PINNIPED_TEST_USER_GROUPS=${test_groups} export PINNIPED_TEST_USER_TOKEN=${test_username}:${test_password} export PINNIPED_TEST_WEBHOOK_ENDPOINT=${webhook_url} export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle} -export PINNIPED_SUPERVISOR_NAMESPACE=${supervisor_namespace} -export PINNIPED_SUPERVISOR_APP_NAME=${supervisor_app_name} +export PINNIPED_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace} +export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name} export PINNIPED_TEST_SUPERVISOR_ADDRESS="localhost:12345" -read -r -d '' PINNIPED_CLUSTER_CAPABILITY_YAML << PINNIPED_CLUSTER_CAPABILITY_YAML_EOF || true +read -r -d '' PINNIPED_TEST_CLUSTER_CAPABILITY_YAML << PINNIPED_TEST_CLUSTER_CAPABILITY_YAML_EOF || true ${pinniped_cluster_capability_file_content} -PINNIPED_CLUSTER_CAPABILITY_YAML_EOF +PINNIPED_TEST_CLUSTER_CAPABILITY_YAML_EOF -export PINNIPED_CLUSTER_CAPABILITY_YAML +export PINNIPED_TEST_CLUSTER_CAPABILITY_YAML EOF # @@ -298,7 +298,7 @@ log_note " cd $pinniped_path" log_note ' source /tmp/integration-test-env && go test -v -count 1 ./test/integration' log_note log_note 'Want to run integration tests in GoLand? Copy/paste this "Environment" value for GoLand run configurations:' -log_note " ${goland_vars}PINNIPED_CLUSTER_CAPABILITY_FILE=${kind_capabilities_file}" +log_note " ${goland_vars}PINNIPED_TEST_CLUSTER_CAPABILITY_FILE=${kind_capabilities_file}" log_note if ! tilt_mode; then diff --git a/test/integration/cli_test.go b/test/integration/cli_test.go index 1e3372620..c1b13c748 100644 --- a/test/integration/cli_test.go +++ b/test/integration/cli_test.go @@ -8,7 +8,6 @@ import ( "os" "os/exec" "path/filepath" - "strings" "testing" "time" @@ -26,49 +25,25 @@ func TestCLI(t *testing.T) { idp := library.CreateTestWebhookIDP(ctx, t) - // Remove all Pinniped environment variables for the remainder of this test - // because some of their names clash with the env vars expected by our - // kubectl exec plugin. We would like this test to prove that the exec - // plugin receives all of the necessary env vars via the auto-generated - // kubeconfig from the Pinniped CLI. - initialEnvVars := make(map[string]string) - for _, e := range os.Environ() { - pair := strings.SplitN(e, "=", 2) - name := pair[0] - value := pair[1] - if strings.HasPrefix(name, "PINNIPED_") { - initialEnvVars[name] = value - err := os.Unsetenv(name) - require.NoError(t, err) - } - } - // Put them back for other tests to use after this one - t.Cleanup(func() { - for k, v := range initialEnvVars { - err := os.Setenv(k, v) - require.NoError(t, err) - } - }) - // Build pinniped CLI. pinnipedExe, cleanupFunc := buildPinnipedCLI(t) defer cleanupFunc() // Run pinniped CLI to get kubeconfig. - kubeConfigYAML := runPinnipedCLI(t, pinnipedExe, env.TestUser.Token, env.Namespace, "webhook", idp.Name) + kubeConfigYAML := runPinnipedCLI(t, pinnipedExe, env.TestUser.Token, env.ConciergeNamespace, "webhook", idp.Name) // In addition to the client-go based testing below, also try the kubeconfig // with kubectl to validate that it works. adminClient := library.NewClientset(t) t.Run( "access as user with kubectl", - library.AccessAsUserWithKubectlTest(ctx, adminClient, kubeConfigYAML, env.TestUser.ExpectedUsername, env.Namespace), + library.AccessAsUserWithKubectlTest(ctx, adminClient, kubeConfigYAML, env.TestUser.ExpectedUsername, env.ConciergeNamespace), ) for _, group := range env.TestUser.ExpectedGroups { group := group t.Run( "access as group "+group+" with kubectl", - library.AccessAsGroupWithKubectlTest(ctx, adminClient, kubeConfigYAML, group, env.Namespace), + library.AccessAsGroupWithKubectlTest(ctx, adminClient, kubeConfigYAML, group, env.ConciergeNamespace), ) } diff --git a/test/integration/client_test.go b/test/integration/client_test.go index ebf1776b1..a5dba407b 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -72,7 +72,7 @@ func TestClient(t *testing.T) { var resp *clientauthenticationv1beta1.ExecCredential assert.Eventually(t, func() bool { - resp, err = client.ExchangeToken(ctx, env.Namespace, idp, env.TestUser.Token, string(clientConfig.CAData), clientConfig.Host) + resp, err = client.ExchangeToken(ctx, env.ConciergeNamespace, idp, env.TestUser.Token, string(clientConfig.CAData), clientConfig.Host) return err == nil }, 10*time.Second, 500*time.Millisecond) require.NoError(t, err) diff --git a/test/integration/concierge_api_serving_certs_test.go b/test/integration/concierge_api_serving_certs_test.go index c291ca70f..42fb8d754 100644 --- a/test/integration/concierge_api_serving_certs_test.go +++ b/test/integration/concierge_api_serving_certs_test.go @@ -83,7 +83,7 @@ func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) { const apiServiceName = "v1alpha1.login.pinniped.dev" // Get the initial auto-generated version of the Secret. - secret, err := kubeClient.CoreV1().Secrets(env.Namespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{}) + secret, err := kubeClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{}) require.NoError(t, err) initialCACert := secret.Data["caCertificate"] initialPrivateKey := secret.Data["tlsPrivateKey"] @@ -98,11 +98,11 @@ func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) { require.Equal(t, initialCACert, apiService.Spec.CABundle) // Force rotation to happen. - require.NoError(t, test.forceRotation(ctx, kubeClient, env.Namespace)) + require.NoError(t, test.forceRotation(ctx, kubeClient, env.ConciergeNamespace)) // Expect that the Secret comes back right away with newly minted certs. secretIsRegenerated := func() bool { - secret, err = kubeClient.CoreV1().Secrets(env.Namespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{}) + secret, err = kubeClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{}) return err == nil } assert.Eventually(t, secretIsRegenerated, 10*time.Second, 250*time.Millisecond) @@ -133,7 +133,7 @@ func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) { // pod has rotated their cert, but not the other ones sitting behind the service. aggregatedAPIWorking := func() bool { for i := 0; i < 10; i++ { - _, err = pinnipedClient.LoginV1alpha1().TokenCredentialRequests(env.Namespace).Create(ctx, &loginv1alpha1.TokenCredentialRequest{ + _, err = pinnipedClient.LoginV1alpha1().TokenCredentialRequests(env.ConciergeNamespace).Create(ctx, &loginv1alpha1.TokenCredentialRequest{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{}, Spec: loginv1alpha1.TokenCredentialRequestSpec{Token: "not a good token"}, diff --git a/test/integration/concierge_availability_test.go b/test/integration/concierge_availability_test.go index 16b338e65..a47055138 100644 --- a/test/integration/concierge_availability_test.go +++ b/test/integration/concierge_availability_test.go @@ -23,7 +23,7 @@ func TestGetDeployment(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - appDeployment, err := client.AppsV1().Deployments(env.Namespace).Get(ctx, env.AppName, metav1.GetOptions{}) + appDeployment, err := client.AppsV1().Deployments(env.ConciergeNamespace).Get(ctx, env.ConciergeAppName, metav1.GetOptions{}) require.NoError(t, err) cond := getDeploymentCondition(appDeployment.Status, appsv1.DeploymentAvailable) diff --git a/test/integration/concierge_credentialissuerconfig_test.go b/test/integration/concierge_credentialissuerconfig_test.go index a5323b6f2..5616b09d3 100644 --- a/test/integration/concierge_credentialissuerconfig_test.go +++ b/test/integration/concierge_credentialissuerconfig_test.go @@ -27,7 +27,7 @@ func TestCredentialIssuerConfig(t *testing.T) { t.Run("test successful CredentialIssuerConfig", func(t *testing.T) { actualConfigList, err := client. ConfigV1alpha1(). - CredentialIssuerConfigs(env.Namespace). + CredentialIssuerConfigs(env.ConciergeNamespace). List(ctx, metav1.ListOptions{}) require.NoError(t, err) diff --git a/test/integration/concierge_credentialrequest_test.go b/test/integration/concierge_credentialrequest_test.go index 75b95719f..e1a57a0ab 100644 --- a/test/integration/concierge_credentialrequest_test.go +++ b/test/integration/concierge_credentialrequest_test.go @@ -145,9 +145,9 @@ func makeRequest(ctx context.Context, t *testing.T, spec loginv1alpha1.TokenCred ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - return client.LoginV1alpha1().TokenCredentialRequests(env.Namespace).Create(ctx, &loginv1alpha1.TokenCredentialRequest{ + return client.LoginV1alpha1().TokenCredentialRequests(env.ConciergeNamespace).Create(ctx, &loginv1alpha1.TokenCredentialRequest{ TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Namespace: env.Namespace}, + ObjectMeta: metav1.ObjectMeta{Namespace: env.ConciergeNamespace}, Spec: spec, }, metav1.CreateOptions{}) } diff --git a/test/integration/concierge_kubecertagent_test.go b/test/integration/concierge_kubecertagent_test.go index 4dfb71215..6549fa4da 100644 --- a/test/integration/concierge_kubecertagent_test.go +++ b/test/integration/concierge_kubecertagent_test.go @@ -37,7 +37,7 @@ func TestKubeCertAgent(t *testing.T) { // We can pretty safely assert there should be more than 1, since there should be a // kube-cert-agent pod per kube-controller-manager pod, and there should probably be at least // 1 kube-controller-manager for this to be a working kube API. - originalAgentPods, err := kubeClient.CoreV1().Pods(env.Namespace).List(ctx, metav1.ListOptions{ + originalAgentPods, err := kubeClient.CoreV1().Pods(env.ConciergeNamespace).List(ctx, metav1.ListOptions{ LabelSelector: kubeCertAgentLabelSelector, }) require.NoError(t, err) @@ -46,7 +46,7 @@ func TestKubeCertAgent(t *testing.T) { agentPodsReconciled := func() bool { var currentAgentPods *corev1.PodList - currentAgentPods, err = kubeClient.CoreV1().Pods(env.Namespace).List(ctx, metav1.ListOptions{ + currentAgentPods, err = kubeClient.CoreV1().Pods(env.ConciergeNamespace).List(ctx, metav1.ListOptions{ LabelSelector: kubeCertAgentLabelSelector, }) @@ -90,7 +90,7 @@ func TestKubeCertAgent(t *testing.T) { updatedAgentPod.Spec.Tolerations, corev1.Toleration{Key: "fake-toleration"}, ) - _, err = kubeClient.CoreV1().Pods(env.Namespace).Update(ctx, updatedAgentPod, metav1.UpdateOptions{}) + _, err = kubeClient.CoreV1().Pods(env.ConciergeNamespace).Update(ctx, updatedAgentPod, metav1.UpdateOptions{}) require.NoError(t, err) // Make sure the original pods come back. @@ -102,7 +102,7 @@ func TestKubeCertAgent(t *testing.T) { // Delete the first pod. The controller should see it, and flip it back. err = kubeClient. CoreV1(). - Pods(env.Namespace). + Pods(env.ConciergeNamespace). Delete(ctx, originalAgentPods.Items[0].Name, metav1.DeleteOptions{}) require.NoError(t, err) diff --git a/test/library/client.go b/test/library/client.go index a208f5bc6..a5cdcdb38 100644 --- a/test/library/client.go +++ b/test/library/client.go @@ -113,7 +113,7 @@ func newAnonymousClientRestConfigWithCertAndKeyAdded(t *testing.T, clientCertifi return config } -// CreateTestWebhookIDP creates and returns a test WebhookIdentityProvider in $PINNIPED_NAMESPACE, which will be +// CreateTestWebhookIDP creates and returns a test WebhookIdentityProvider in $PINNIPED_TEST_CONCIERGE_NAMESPACE, which will be // automatically deleted at the end of the current test's lifetime. It returns a corev1.TypedLocalObjectReference which // descibes the test IDP within the test namespace. func CreateTestWebhookIDP(ctx context.Context, t *testing.T) corev1.TypedLocalObjectReference { @@ -121,7 +121,7 @@ func CreateTestWebhookIDP(ctx context.Context, t *testing.T) corev1.TypedLocalOb testEnv := IntegrationEnv(t) client := NewPinnipedClientset(t) - webhooks := client.IDPV1alpha1().WebhookIdentityProviders(testEnv.Namespace) + webhooks := client.IDPV1alpha1().WebhookIdentityProviders(testEnv.ConciergeNamespace) createContext, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() diff --git a/test/library/env.go b/test/library/env.go index 257bd3219..f97aa5f85 100644 --- a/test/library/env.go +++ b/test/library/env.go @@ -25,14 +25,15 @@ const ( type TestEnv struct { t *testing.T - Namespace string `json:"namespace"` + ConciergeNamespace string `json:"conciergeNamespace"` SupervisorNamespace string `json:"supervisorNamespace"` - AppName string `json:"appName"` + ConciergeAppName string `json:"conciergeAppName"` SupervisorAppName string `json:"supervisorAppName"` Capabilities map[TestClusterCapability]bool `json:"capabilities"` TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"` SupervisorAddress string `json:"supervisorAddress"` - TestUser struct { + + TestUser struct { Token string `json:"token"` ExpectedUsername string `json:"expectedUsername"` ExpectedGroups []string `json:"expectedGroups"` @@ -45,11 +46,11 @@ func IntegrationEnv(t *testing.T) *TestEnv { t.Helper() SkipUnlessIntegration(t) - capabilitiesDescriptionYAML := os.Getenv("PINNIPED_CLUSTER_CAPABILITY_YAML") - capabilitiesDescriptionFile := os.Getenv("PINNIPED_CLUSTER_CAPABILITY_FILE") + capabilitiesDescriptionYAML := os.Getenv("PINNIPED_TEST_CLUSTER_CAPABILITY_YAML") + capabilitiesDescriptionFile := os.Getenv("PINNIPED_TEST_CLUSTER_CAPABILITY_FILE") require.NotEmptyf(t, capabilitiesDescriptionYAML+capabilitiesDescriptionFile, - "must specify either PINNIPED_CLUSTER_CAPABILITY_YAML or PINNIPED_CLUSTER_CAPABILITY_FILE env var for integration tests", + "must specify either PINNIPED_TEST_CLUSTER_CAPABILITY_YAML or PINNIPED_TEST_CLUSTER_CAPABILITY_FILE env var for integration tests", ) if capabilitiesDescriptionYAML == "" { bytes, err := ioutil.ReadFile(capabilitiesDescriptionFile) @@ -68,14 +69,14 @@ func IntegrationEnv(t *testing.T) *TestEnv { return value } - result.Namespace = needEnv("PINNIPED_NAMESPACE") - result.AppName = needEnv("PINNIPED_APP_NAME") + result.ConciergeNamespace = needEnv("PINNIPED_TEST_CONCIERGE_NAMESPACE") + result.ConciergeAppName = needEnv("PINNIPED_TEST_CONCIERGE_APP_NAME") result.TestUser.ExpectedUsername = needEnv("PINNIPED_TEST_USER_USERNAME") result.TestUser.ExpectedGroups = strings.Split(strings.ReplaceAll(needEnv("PINNIPED_TEST_USER_GROUPS"), " ", ""), ",") result.TestUser.Token = needEnv("PINNIPED_TEST_USER_TOKEN") result.TestWebhook.Endpoint = needEnv("PINNIPED_TEST_WEBHOOK_ENDPOINT") - result.SupervisorNamespace = needEnv("PINNIPED_SUPERVISOR_NAMESPACE") - result.SupervisorAppName = needEnv("PINNIPED_SUPERVISOR_APP_NAME") + result.SupervisorNamespace = needEnv("PINNIPED_TEST_SUPERVISOR_NAMESPACE") + result.SupervisorAppName = needEnv("PINNIPED_TEST_SUPERVISOR_APP_NAME") result.SupervisorAddress = needEnv("PINNIPED_TEST_SUPERVISOR_ADDRESS") result.TestWebhook.TLS = &idpv1alpha1.TLSSpec{CertificateAuthorityData: needEnv("PINNIPED_TEST_WEBHOOK_CA_BUNDLE")} result.t = t From 34549b779bd44c22788dc2f4cbaaa1207e439345 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 14:25:34 -0700 Subject: [PATCH 24/28] Make tilt work with the supervisor app and add more uninstall testing - Also continue renaming things related to the concierge app - Enhance the uninstall test to also test uninstalling the supervisor and local-user-authenticator apps --- CONTRIBUTING.md | 6 +- deploy/concierge/values.yaml | 4 +- hack/kind-down.sh | 11 ++ hack/kind-up.sh | 12 ++ hack/lib/tilt/Tiltfile | 103 +++++++++++++----- ...nniped.Dockerfile => concierge.Dockerfile} | 4 +- hack/lib/tilt/nodeport.yaml | 13 +++ hack/lib/tilt/supervisor.Dockerfile | 14 +++ hack/prepare-for-integration-tests.sh | 53 +++++---- hack/tilt-up.sh | 4 +- pkg/config/config_test.go | 18 +-- .../concierge_api_serving_certs_test.go | 3 +- ...ery_test.go => kube_api_discovery_test.go} | 0 13 files changed, 173 insertions(+), 72 deletions(-) create mode 100755 hack/kind-down.sh create mode 100755 hack/kind-up.sh rename hack/lib/tilt/{pinniped.Dockerfile => concierge.Dockerfile} (68%) create mode 100644 hack/lib/tilt/nodeport.yaml create mode 100644 hack/lib/tilt/supervisor.Dockerfile rename test/integration/{concierge_api_discovery_test.go => kube_api_discovery_test.go} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29e4d7ce3..553a8745a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,7 +109,7 @@ docker build . 1. Create a local Kubernetes cluster using `kind`: ```bash - kind create cluster --image kindest/node:v1.18.8 + ./hack/kind-up.sh ``` 1. Install Pinniped and supporting dependencies using `tilt`: @@ -123,11 +123,11 @@ docker build . 1. Run the Pinniped integration tests: ```bash - source ./hack/lib/tilt/integration-test.env && go test -v -count 1 ./test/integration + source /tmp/integration-test-env && go test -v -count 1 ./test/integration ``` To uninstall the test environment, run `./hack/tilt-down.sh`. -To destroy the local Kubernetes cluster, run `kind delete cluster`. +To destroy the local Kubernetes cluster, run `./hack/kind-down.sh`. ### Observing Tests on the Continuous Integration Environment diff --git a/deploy/concierge/values.yaml b/deploy/concierge/values.yaml index 0a25f7ee1..49271f0fe 100644 --- a/deploy/concierge/values.yaml +++ b/deploy/concierge/values.yaml @@ -4,8 +4,8 @@ #@data/values --- -app_name: pinniped -namespace: pinniped +app_name: pinniped-concierge +namespace: pinniped-concierge #! Specify how many replicas of the Pinniped server to run. replicas: 2 diff --git a/hack/kind-down.sh b/hack/kind-down.sh new file mode 100755 index 000000000..7c96dca85 --- /dev/null +++ b/hack/kind-down.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# Copyright 2020 the Pinniped contributors. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail +ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" + +cd "${ROOT}" + +kind delete cluster --name pinniped diff --git a/hack/kind-up.sh b/hack/kind-up.sh new file mode 100755 index 000000000..72da2d6d2 --- /dev/null +++ b/hack/kind-up.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Copyright 2020 the Pinniped contributors. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail +ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" + +cd "${ROOT}" + +# To choose a specific version of kube, add this option to the command below: `--image kindest/node:v1.18.8` +kind create cluster --config "hack/lib/kind-config/single-node.yaml" --name pinniped diff --git a/hack/lib/tilt/Tiltfile b/hack/lib/tilt/Tiltfile index 16b770772..be2c87923 100644 --- a/hack/lib/tilt/Tiltfile +++ b/hack/lib/tilt/Tiltfile @@ -15,6 +15,10 @@ local_resource( deps=['../../../cmd', '../../../internal', '../../../pkg', '../../../generated'], ) +# +# local-user-authenticator app +# + # Build a container image for local-user-authenticator, with live-update enabled. docker_build_with_restart('image/local-user-auth', '.', dockerfile='local-user-authenticator.Dockerfile', @@ -39,59 +43,106 @@ k8s_resource( 'local-user-authenticator:namespace', 'local-user-authenticator:serviceaccount', 'local-user-authenticator:role', - 'local-user-authenticator:rolebinding', + 'local-user-authenticator:rolebinding' ], ) -# Build a container image for the Pinniped server, with live-update enabled. -docker_build_with_restart('image/pinniped', '.', - dockerfile='pinniped.Dockerfile', - entrypoint=['/usr/local/bin/pinniped-server'], - live_update=[sync('./build/pinniped-server', '/usr/local/bin/pinniped-server')], - only=['./build/pinniped-server'], +# +# Supervisor app +# + +# Build a container image for supervisor, with live-update enabled. +docker_build_with_restart('image/supervisor', '.', + dockerfile='supervisor.Dockerfile', + entrypoint=['/usr/local/bin/pinniped-supervisor'], + live_update=[sync('./build/pinniped-supervisor', '/usr/local/bin/pinniped-supervisor')], + only=['./build/pinniped-supervisor'], ) -# Render the Pinniped server installation manifest using ytt. +# Render the supervisor installation manifest using ytt. +k8s_yaml(local([ + 'ytt', + '--file', '../../../deploy/supervisor', + '--data-value', 'image_repo=image/supervisor', + '--data-value', 'image_tag=tilt-dev', + '--data-value-yaml', 'replicas=1' +])) + +# Collect all the deployed supervisor resources under a "supervisor" resource tab. +k8s_resource( + workload='pinniped-supervisor', + new_name='supervisor', + objects=[ + 'oidcproviderconfigs.config.pinniped.dev:customresourcedefinition', + 'pinniped-supervisor-static-config:configmap', + 'pinniped-supervisor:namespace', + 'pinniped-supervisor:role', + 'pinniped-supervisor:rolebinding', + 'pinniped-supervisor:serviceaccount', + ], +) + +# Build a container image for the Concierge server, with live-update enabled. +docker_build_with_restart('image/concierge', '.', + dockerfile='concierge.Dockerfile', + entrypoint=['/usr/local/bin/pinniped-concierge'], + live_update=[sync('./build/pinniped-concierge', '/usr/local/bin/pinniped-concierge')], + only=['./build/pinniped-concierge'], +) + +k8s_yaml('nodeport.yaml') + +# +# Concierge app +# + +# Render the Concierge server installation manifest using ytt. k8s_yaml(local([ 'sh', '-c', 'ytt --file ../../../deploy/concierge ' + + '--data-value app_name=pinniped-concierge ' + '--data-value namespace=integration ' + - '--data-value image_repo=image/pinniped ' + + '--data-value image_repo=image/concierge ' + '--data-value image_tag=tilt-dev ' + '--data-value kube_cert_agent_image=debian:10.5-slim ' + '--data-value discovery_url=$(TERM=dumb kubectl cluster-info | awk \'/Kubernetes master/ {print $NF}\') ' + '--data-value-yaml replicas=1' ])) -# Collect all the deployed local-user-authenticator resources under a "deploy/pinniped" resource tab. +# Collect all the deployed local-user-authenticator resources under a "concierge" resource tab. k8s_resource( - workload='pinniped', + workload='pinniped-concierge', + new_name='concierge', objects=[ 'integration:namespace', + 'pinniped-concierge-aggregated-api-server:clusterrole', + 'pinniped-concierge-aggregated-api-server:clusterrolebinding', + 'pinniped-concierge-aggregated-api-server:role', + 'pinniped-concierge-aggregated-api-server:rolebinding', + 'pinniped-concierge-cluster-info-lister-watcher:role', + 'pinniped-concierge-cluster-info-lister-watcher:rolebinding', + 'pinniped-concierge-config:configmap', + 'pinniped-concierge-create-token-credential-requests:clusterrole', + 'pinniped-concierge-create-token-credential-requests:clusterrolebinding', + 'pinniped-concierge-extension-apiserver-authentication-reader:rolebinding', + 'pinniped-concierge-kube-system-pod-read:role', + 'pinniped-concierge-kube-system-pod-read:rolebinding', + 'pinniped-concierge:clusterrolebinding', + 'pinniped-concierge:serviceaccount', 'credentialissuerconfigs.config.pinniped.dev:customresourcedefinition', 'webhookidentityproviders.idp.pinniped.dev:customresourcedefinition', - 'pinniped:serviceaccount', - 'pinniped-aggregated-api-server:role', - 'pinniped-kube-system-pod-read:role', - 'pinniped-cluster-info-lister-watcher:role', - 'pinniped-aggregated-api-server:clusterrole', - 'pinniped-create-token-credential-requests:clusterrole', - 'pinniped-aggregated-api-server:rolebinding', - 'pinniped-kube-system-pod-read:rolebinding', - 'pinniped-extension-apiserver-authentication-reader:rolebinding', - 'pinniped-cluster-info-lister-watcher:rolebinding', - 'pinniped-aggregated-api-server:clusterrolebinding', - 'pinniped-create-token-credential-requests:clusterrolebinding', - 'pinniped:clusterrolebinding', - 'pinniped-config:configmap', 'v1alpha1.login.pinniped.dev:apiservice', ], ) +# +# Finish setting up cluster and creating integration test env file +# + # Collect environment variables needed to run our integration test suite. local_resource( 'test-env', 'TILT_MODE=yes ../../prepare-for-integration-tests.sh', - resource_deps=['local-user-auth', 'pinniped'], + resource_deps=['local-user-auth', 'concierge', 'supervisor'], deps=['../../prepare-for-integration-tests.sh'], ) diff --git a/hack/lib/tilt/pinniped.Dockerfile b/hack/lib/tilt/concierge.Dockerfile similarity index 68% rename from hack/lib/tilt/pinniped.Dockerfile rename to hack/lib/tilt/concierge.Dockerfile index 8bbc4d136..3ddf40335 100644 --- a/hack/lib/tilt/pinniped.Dockerfile +++ b/hack/lib/tilt/concierge.Dockerfile @@ -5,10 +5,10 @@ FROM debian:10.5-slim # Copy the binary which was built outside the container. -COPY build/pinniped-server /usr/local/bin/pinniped-server +COPY build/pinniped-concierge /usr/local/bin/pinniped-concierge # Document the port EXPOSE 443 # Set the entrypoint -ENTRYPOINT ["/usr/local/bin/pinniped-server"] +ENTRYPOINT ["/usr/local/bin/pinniped-concierge"] diff --git a/hack/lib/tilt/nodeport.yaml b/hack/lib/tilt/nodeport.yaml new file mode 100644 index 000000000..ddad93253 --- /dev/null +++ b/hack/lib/tilt/nodeport.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: pinniped-supervisor-node-port + namespace: pinniped-supervisor +spec: + type: NodePort + selector: + app: pinniped-supervisor + ports: + - port: 80 + targetPort: 80 + nodePort: 31234 diff --git a/hack/lib/tilt/supervisor.Dockerfile b/hack/lib/tilt/supervisor.Dockerfile new file mode 100644 index 000000000..22d7e2040 --- /dev/null +++ b/hack/lib/tilt/supervisor.Dockerfile @@ -0,0 +1,14 @@ +# Copyright 2020 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 + +# Use a runtime image based on Debian slim +FROM debian:10.5-slim + +# Copy the binary which was built outside the container. +COPY build/pinniped-supervisor /usr/local/bin/pinniped-supervisor + +# Document the port +EXPOSE 443 + +# Set the entrypoint +ENTRYPOINT ["/usr/local/bin/pinniped-supervisor"] diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index f792ce24b..bf7c299df 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -110,17 +110,17 @@ fi if ! tilt_mode; then if [[ "$clean_kind" == "yes" ]]; then log_note "Deleting running kind clusters to prepare from a clean slate..." - kind delete cluster + kind delete cluster --name pinniped fi # # Setup kind and build the app # log_note "Checking for running kind clusters..." - if ! kind get clusters | grep -q -e '^kind$'; then + if ! kind get clusters | grep -q -e '^pinniped$'; then log_note "Creating a kind cluster..." # single-node.yaml exposes node port 31234 as localhost:12345 - kind create cluster --config "$pinniped_path/hack/lib/kind-config/single-node.yaml" + kind create cluster --config "$pinniped_path/hack/lib/kind-config/single-node.yaml" --name pinniped else if ! kubectl cluster-info | grep master | grep -q 127.0.0.1; then log_error "Seems like your kubeconfig is not targeting a local cluster." @@ -157,7 +157,7 @@ if ! tilt_mode; then # Load it into the cluster log_note "Loading the app's container image into the kind cluster..." - kind load docker-image "$registry_repo_tag" + kind load docker-image "$registry_repo_tag" --name pinniped manifest=/tmp/manifest.yaml @@ -178,7 +178,6 @@ if ! tilt_mode; then fi - test_username="test-username" test_groups="test-group-0,test-group-1" set +o pipefail @@ -203,18 +202,19 @@ kubectl create secret generic "$test_username" \ supervisor_app_name="pinniped-supervisor" supervisor_namespace="pinniped-supervisor" -pushd deploy/supervisor >/dev/null +if ! tilt_mode; then + pushd deploy/supervisor >/dev/null -log_note "Deploying the Pinniped Supervisor app to the cluster..." -ytt --file . \ - --data-value "app_name=$supervisor_app_name" \ - --data-value "namespace=$supervisor_namespace" \ - --data-value "image_repo=$registry_repo" \ - --data-value "image_tag=$tag" >"$manifest" + log_note "Deploying the Pinniped Supervisor app to the cluster..." + ytt --file . \ + --data-value "app_name=$supervisor_app_name" \ + --data-value "namespace=$supervisor_namespace" \ + --data-value "image_repo=$registry_repo" \ + --data-value "image_tag=$tag" >"$manifest" -kapp deploy --yes --app "pinniped-supervisor" --diff-changes --file "$manifest" + kapp deploy --yes --app "$supervisor_app_name" --diff-changes --file "$manifest" -log_note "Adding NodePort service to expose the Pinniped Supervisor app on the kind node..." + log_note "Adding NodePort service to expose the Pinniped Supervisor app on the kind node..." cat </dev/null + popd >/dev/null +fi # # Deploy Pinniped # -app_name="pinniped" -namespace="integration" +concierge_app_name="pinniped-concierge" +concierge_namespace="integration" webhook_url="https://local-user-authenticator.local-user-authenticator.svc/authenticate" webhook_ca_bundle="$(kubectl get secret local-user-authenticator-tls-serving-certificate --namespace local-user-authenticator -o 'jsonpath={.data.caCertificate}')" discovery_url="$(TERM=dumb kubectl cluster-info | awk '/Kubernetes master/ {print $NF}')" if ! tilt_mode; then - # - # Deploy Pinniped - # pushd deploy/concierge >/dev/null log_note "Deploying the Pinniped app to the cluster..." ytt --file . \ - --data-value "app_name=$app_name" \ - --data-value "namespace=$namespace" \ + --data-value "app_name=$concierge_app_name" \ + --data-value "namespace=$concierge_namespace" \ --data-value "image_repo=$registry_repo" \ --data-value "image_tag=$tag" \ --data-value "discovery_url=$discovery_url" >"$manifest" - kapp deploy --yes --app "$app_name" --diff-changes --file "$manifest" + kapp deploy --yes --app "$concierge_app_name" --diff-changes --file "$manifest" popd >/dev/null fi @@ -269,8 +267,8 @@ pinniped_cluster_capability_file_content=$(cat "$kind_capabilities_file") cat </tmp/integration-test-env # The following env vars should be set before running 'go test -v -count 1 ./test/...' -export PINNIPED_TEST_CONCIERGE_NAMESPACE=${namespace} -export PINNIPED_TEST_CONCIERGE_APP_NAME=${app_name} +export PINNIPED_TEST_CONCIERGE_NAMESPACE=${concierge_namespace} +export PINNIPED_TEST_CONCIERGE_APP_NAME=${concierge_app_name} export PINNIPED_TEST_USER_USERNAME=${test_username} export PINNIPED_TEST_USER_GROUPS=${test_groups} export PINNIPED_TEST_USER_TOKEN=${test_username}:${test_password} @@ -304,6 +302,7 @@ log_note if ! tilt_mode; then log_note "You can rerun this script to redeploy local production code changes while you are working." log_note - log_note "To delete the deployments, run 'kapp delete -a local-user-authenticator -y && kapp delete -a pinniped -y'." - log_note "When you're finished, use 'kind delete cluster' to tear down the cluster." + log_note "To delete the deployments, run:" + log_note " kapp delete -a local-user-authenticator -y && kapp delete -a $concierge_app_name -y && kapp delete -a $supervisor_app_name -y" + log_note "When you're finished, use 'kind delete cluster --name pinniped' to tear down the cluster." fi diff --git a/hack/tilt-up.sh b/hack/tilt-up.sh index acbf5a3ff..85a7b8e48 100755 --- a/hack/tilt-up.sh +++ b/hack/tilt-up.sh @@ -5,5 +5,7 @@ set -euo pipefail ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" + cd "${ROOT}" -exec tilt up -f ./hack/lib/tilt/Tiltfile --stream + +exec tilt up -f ./hack/lib/tilt/Tiltfile diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index c641c515a..8f9dd202e 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -32,7 +32,7 @@ func TestFromPath(t *testing.T) { durationSeconds: 3600 renewBeforeSeconds: 2400 names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuerConfig: pinniped-config apiService: pinniped-api kubeCertAgentPrefix: kube-cert-agent-prefix @@ -52,7 +52,7 @@ func TestFromPath(t *testing.T) { }, }, NamesConfig: api.NamesConfigSpec{ - ServingCertificateSecret: "pinniped-api-tls-serving-certificate", + ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate", CredentialIssuerConfig: "pinniped-config", APIService: "pinniped-api", }, @@ -68,7 +68,7 @@ func TestFromPath(t *testing.T) { yaml: here.Doc(` --- names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuerConfig: pinniped-config apiService: pinniped-api `), @@ -83,7 +83,7 @@ func TestFromPath(t *testing.T) { }, }, NamesConfig: api.NamesConfigSpec{ - ServingCertificateSecret: "pinniped-api-tls-serving-certificate", + ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate", CredentialIssuerConfig: "pinniped-config", APIService: "pinniped-api", }, @@ -103,7 +103,7 @@ func TestFromPath(t *testing.T) { yaml: here.Doc(` --- names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuerConfig: pinniped-config `), wantError: "validate names: missing required names: apiService", @@ -113,7 +113,7 @@ func TestFromPath(t *testing.T) { yaml: here.Doc(` --- names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate apiService: pinniped-api `), wantError: "validate names: missing required names: credentialIssuerConfig", @@ -137,7 +137,7 @@ func TestFromPath(t *testing.T) { durationSeconds: 2400 renewBeforeSeconds: 3600 names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuerConfig: pinniped-config apiService: pinniped-api `), @@ -152,7 +152,7 @@ func TestFromPath(t *testing.T) { durationSeconds: 2400 renewBeforeSeconds: -10 names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuerConfig: pinniped-config apiService: pinniped-api `), @@ -167,7 +167,7 @@ func TestFromPath(t *testing.T) { durationSeconds: 2400 renewBeforeSeconds: -10 names: - servingCertificateSecret: pinniped-api-tls-serving-certificate + servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate credentialIssuerConfig: pinniped-config apiService: pinniped-api `), diff --git a/test/integration/concierge_api_serving_certs_test.go b/test/integration/concierge_api_serving_certs_test.go index 42fb8d754..94f40e127 100644 --- a/test/integration/concierge_api_serving_certs_test.go +++ b/test/integration/concierge_api_serving_certs_test.go @@ -20,8 +20,7 @@ import ( func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) { env := library.IntegrationEnv(t) - - const defaultServingCertResourceName = "pinniped-api-tls-serving-certificate" + defaultServingCertResourceName := env.ConciergeAppName + "-api-tls-serving-certificate" tests := []struct { name string diff --git a/test/integration/concierge_api_discovery_test.go b/test/integration/kube_api_discovery_test.go similarity index 100% rename from test/integration/concierge_api_discovery_test.go rename to test/integration/kube_api_discovery_test.go From 354b922e48972b19a291d6fa4699ca6f17d21f5a Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 16:00:11 -0700 Subject: [PATCH 25/28] Allow creation of different Service types in Supervisor ytt templates - Tiltfile and prepare-for-integration-tests.sh both specify the NodePort Service using `--data-value-yaml 'service_nodeport_port=31234'` - Also rename the namespaces used by the Concierge and Supervisor apps during integration tests running locally --- deploy/supervisor/deployment.yaml | 38 +---------------- deploy/supervisor/service.yaml | 59 +++++++++++++++++++++++++++ deploy/supervisor/values.yaml | 7 ++++ hack/lib/tilt/Tiltfile | 45 +++++++++++--------- hack/lib/tilt/nodeport.yaml | 13 ------ hack/prepare-for-integration-tests.sh | 22 ++-------- 6 files changed, 97 insertions(+), 87 deletions(-) create mode 100644 deploy/supervisor/service.yaml delete mode 100644 hack/lib/tilt/nodeport.yaml diff --git a/deploy/supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml index cd4079c8a..459276fbb 100644 --- a/deploy/supervisor/deployment.yaml +++ b/deploy/supervisor/deployment.yaml @@ -76,8 +76,8 @@ spec: command: #! override the default entrypoint - /usr/local/bin/pinniped-supervisor args: - - /etc/podinfo #! TODO proper flag parsing instead of positional - - /etc/config/pinniped.yaml #! TODO proper flag parsing instead of positional + - /etc/podinfo + - /etc/config/pinniped.yaml resources: requests: memory: "128Mi" @@ -86,24 +86,6 @@ spec: mountPath: /etc/config - name: podinfo mountPath: /etc/podinfo -#! livenessProbe: -#! httpGet: -#! path: /healthz -#! port: 443 -#! scheme: HTTPS -#! initialDelaySeconds: 2 -#! timeoutSeconds: 15 -#! periodSeconds: 10 -#! failureThreshold: 5 -#! readinessProbe: -#! httpGet: -#! path: /healthz -#! port: 443 -#! scheme: HTTPS -#! initialDelaySeconds: 2 -#! timeoutSeconds: 3 -#! periodSeconds: 10 -#! failureThreshold: 3 volumes: - name: config-volume configMap: @@ -128,19 +110,3 @@ spec: matchLabels: app: #@ data.values.app_name topologyKey: kubernetes.io/hostname ---- -apiVersion: v1 -kind: Service -metadata: - name: #@ data.values.app_name - namespace: #@ data.values.namespace - labels: - app: #@ data.values.app_name -spec: - type: ClusterIP - selector: - app: #@ data.values.app_name - ports: - - protocol: TCP - port: 80 - targetPort: 80 diff --git a/deploy/supervisor/service.yaml b/deploy/supervisor/service.yaml new file mode 100644 index 000000000..6acbe7fc5 --- /dev/null +++ b/deploy/supervisor/service.yaml @@ -0,0 +1,59 @@ +#@ load("@ytt:data", "data") + +#@ if data.values.service_nodeport_port: +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ data.values.app_name + "-nodeport" + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +spec: + type: NodePort + selector: + app: #@ data.values.app_name + ports: + - protocol: TCP + port: 80 + targetPort: 80 + nodePort: #@ data.values.service_nodeport_port +#@ end + +#@ if data.values.service_clusterip_port: +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ data.values.app_name + "-clusterip" + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +spec: + type: ClusterIP + selector: + app: #@ data.values.app_name + ports: + - protocol: TCP + port: #@ data.values.service_clusterip_port + targetPort: 80 +#@ end + +#@ if data.values.service_loadbalancer_port: +--- +apiVersion: v1 +kind: Service +metadata: + name: #@ data.values.app_name + "-loadbalancer" + namespace: #@ data.values.namespace + labels: + app: #@ data.values.app_name +spec: + type: LoadBalancer + selector: + app: #@ data.values.app_name + ports: + - protocol: TCP + port: #@ data.values.service_loadbalancer_port + targetPort: 80 +#@ end diff --git a/deploy/supervisor/values.yaml b/deploy/supervisor/values.yaml index 6df6efe99..6e74ca362 100644 --- a/deploy/supervisor/values.yaml +++ b/deploy/supervisor/values.yaml @@ -20,3 +20,10 @@ image_tag: latest #! Typically the value would be the output of: kubectl create secret docker-registry x --docker-server=https://example.io --docker-username="USERNAME" --docker-password="PASSWORD" --dry-run=client -o json | jq -r '.data[".dockerconfigjson"]' #! Optional. image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}} + +#! Specify how to expose the Supervisor app as a Service. +#! Typically you would set a value for only one of the following. +#! Setting any of these values means that a Service of that type will be created. +service_nodeport_port: #! e.g. 31234 +service_loadbalancer_port: #! e.g. 443 +service_clusterip_port: #! e.g. 443 diff --git a/hack/lib/tilt/Tiltfile b/hack/lib/tilt/Tiltfile index be2c87923..26213238d 100644 --- a/hack/lib/tilt/Tiltfile +++ b/hack/lib/tilt/Tiltfile @@ -8,15 +8,18 @@ os.putenv('GOARCH', 'amd64') os.putenv('CGO_ENABLED', '0') os.putenv('KUBE_GIT_VERSION', 'v0.0.0') +##################################################################################################### # Compile all of our ./cmd/... binaries. +# + local_resource( 'compile', 'cd ../../../ && mkdir -p ./hack/lib/tilt/build && go build -v -ldflags "$(hack/get-ldflags.sh)" -o ./hack/lib/tilt/build ./cmd/...', deps=['../../../cmd', '../../../internal', '../../../pkg', '../../../generated'], ) -# -# local-user-authenticator app +##################################################################################################### +# Local-user-authenticator app # # Build a container image for local-user-authenticator, with live-update enabled. @@ -37,17 +40,18 @@ k8s_yaml(local([ # Collect all the deployed local-user-authenticator resources under a "local-user-auth" resource tab. k8s_resource( - workload='local-user-authenticator', - new_name='local-user-auth', + workload='local-user-authenticator', # this is the deployment name + new_name='local-user-auth', # this is the name that will appear in the tilt UI objects=[ + # these are the objects that would otherwise appear in the "uncategorized" tab in the tilt UI 'local-user-authenticator:namespace', 'local-user-authenticator:serviceaccount', 'local-user-authenticator:role', - 'local-user-authenticator:rolebinding' + 'local-user-authenticator:rolebinding', ], ) -# +##################################################################################################### # Supervisor app # @@ -63,19 +67,23 @@ docker_build_with_restart('image/supervisor', '.', k8s_yaml(local([ 'ytt', '--file', '../../../deploy/supervisor', + '--data-value', 'app_name=pinniped-supervisor', + '--data-value', 'namespace=supervisor', '--data-value', 'image_repo=image/supervisor', '--data-value', 'image_tag=tilt-dev', - '--data-value-yaml', 'replicas=1' + '--data-value-yaml', 'replicas=1', + '--data-value-yaml', 'service_nodeport_port=31234', ])) # Collect all the deployed supervisor resources under a "supervisor" resource tab. k8s_resource( - workload='pinniped-supervisor', - new_name='supervisor', + workload='pinniped-supervisor', # this is the deployment name + new_name='supervisor', # this is the name that will appear in the tilt UI objects=[ + # these are the objects that would otherwise appear in the "uncategorized" tab in the tilt UI 'oidcproviderconfigs.config.pinniped.dev:customresourcedefinition', 'pinniped-supervisor-static-config:configmap', - 'pinniped-supervisor:namespace', + 'supervisor:namespace', 'pinniped-supervisor:role', 'pinniped-supervisor:rolebinding', 'pinniped-supervisor:serviceaccount', @@ -90,9 +98,7 @@ docker_build_with_restart('image/concierge', '.', only=['./build/pinniped-concierge'], ) -k8s_yaml('nodeport.yaml') - -# +##################################################################################################### # Concierge app # @@ -101,20 +107,21 @@ k8s_yaml(local([ 'sh', '-c', 'ytt --file ../../../deploy/concierge ' + '--data-value app_name=pinniped-concierge ' + - '--data-value namespace=integration ' + + '--data-value namespace=concierge ' + '--data-value image_repo=image/concierge ' + '--data-value image_tag=tilt-dev ' + '--data-value kube_cert_agent_image=debian:10.5-slim ' + '--data-value discovery_url=$(TERM=dumb kubectl cluster-info | awk \'/Kubernetes master/ {print $NF}\') ' + - '--data-value-yaml replicas=1' + '--data-value-yaml replicas=1', ])) # Collect all the deployed local-user-authenticator resources under a "concierge" resource tab. k8s_resource( - workload='pinniped-concierge', - new_name='concierge', + workload='pinniped-concierge', # this is the deployment name + new_name='concierge', # this is the name that will appear in the tilt UI objects=[ - 'integration:namespace', + # these are the objects that would otherwise appear in the "uncategorized" tab in the tilt UI + 'concierge:namespace', 'pinniped-concierge-aggregated-api-server:clusterrole', 'pinniped-concierge-aggregated-api-server:clusterrolebinding', 'pinniped-concierge-aggregated-api-server:role', @@ -135,7 +142,7 @@ k8s_resource( ], ) -# +##################################################################################################### # Finish setting up cluster and creating integration test env file # diff --git a/hack/lib/tilt/nodeport.yaml b/hack/lib/tilt/nodeport.yaml deleted file mode 100644 index ddad93253..000000000 --- a/hack/lib/tilt/nodeport.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: pinniped-supervisor-node-port - namespace: pinniped-supervisor -spec: - type: NodePort - selector: - app: pinniped-supervisor - ports: - - port: 80 - targetPort: 80 - nodePort: 31234 diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index bf7c299df..04aa55317 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -200,7 +200,7 @@ kubectl create secret generic "$test_username" \ # Deploy the Pinniped Supervisor # supervisor_app_name="pinniped-supervisor" -supervisor_namespace="pinniped-supervisor" +supervisor_namespace="supervisor" if ! tilt_mode; then pushd deploy/supervisor >/dev/null @@ -210,27 +210,11 @@ if ! tilt_mode; then --data-value "app_name=$supervisor_app_name" \ --data-value "namespace=$supervisor_namespace" \ --data-value "image_repo=$registry_repo" \ + --data-value-yaml 'service_nodeport_port=31234' \ --data-value "image_tag=$tag" >"$manifest" kapp deploy --yes --app "$supervisor_app_name" --diff-changes --file "$manifest" - log_note "Adding NodePort service to expose the Pinniped Supervisor app on the kind node..." -cat </dev/null fi @@ -238,7 +222,7 @@ fi # Deploy Pinniped # concierge_app_name="pinniped-concierge" -concierge_namespace="integration" +concierge_namespace="concierge" webhook_url="https://local-user-authenticator.local-user-authenticator.svc/authenticate" webhook_ca_bundle="$(kubectl get secret local-user-authenticator-tls-serving-certificate --namespace local-user-authenticator -o 'jsonpath={.data.caCertificate}')" discovery_url="$(TERM=dumb kubectl cluster-info | awk '/Kubernetes master/ {print $NF}')" From 171f3ed9069bc27e74415a8860629f739060383d Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 16:28:34 -0700 Subject: [PATCH 26/28] Add some docs for how to configure the Supervisor app after installing --- deploy/supervisor/README.md | 71 ++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/deploy/supervisor/README.md b/deploy/supervisor/README.md index 7920b6cbd..87db1b9cd 100644 --- a/deploy/supervisor/README.md +++ b/deploy/supervisor/README.md @@ -38,4 +38,73 @@ Either [install `ytt`](https://get-ytt.io/) or use the [container image from Doc ## Configuring After Installing -TODO: Provide some instructions here. +### Exposing the Supervisor App as a Service + +Create a Service to make the app available outside of the cluster. If you installed using `ytt` then you can use +the related `service_*_port` options from [deploy/supervisor/values.yml](values.yaml) to create a Service, instead +of creating them manually as shown below. + +#### Using a LoadBalancer Service + +Using a LoadBalancer Service is probably the easiest way to expose the Supervisor app, if your cluster supports +LoadBalancer Services. For example: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: pinniped-supervisor-loadbalancer + namespace: pinniped-supervisor + labels: + app: pinniped-supervisor +spec: + type: LoadBalancer + selector: + app: pinniped-supervisor + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` + +#### Using a NodePort Service + +A NodePort Service exposes the app as a port on the nodes of the cluster. +This is convenient for use with kind clusters, because kind can +[expose node ports as localhost ports on the host machine](https://kind.sigs.k8s.io/docs/user/configuration/#extra-port-mappings). + +For example: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: pinniped-supervisor-nodeport + namespace: pinniped-supervisor + labels: + app: pinniped-supervisor +spec: + type: NodePort + selector: + app: pinniped-supervisor + ports: + - protocol: TCP + port: 80 + targetPort: 80 + nodePort: 31234 +``` + +### Configuring the Supervisor to Act as an OIDC Provider + +The Supervisor can be configured as an OIDC provider by creating `OIDCProviderConfig` resources +in the same namespace where the Supervisor app was installed. For example: + +```yaml +apiVersion: config.pinniped.dev/v1alpha1 +kind: OIDCProviderConfig +metadata: + name: my-provider + namespace: pinniped-supervisor +spec: + issuer: https://my-issuer.eaxmple.com +``` From d81d395c806d1aa950b366a5af822ed98dc81929 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 18:07:13 -0700 Subject: [PATCH 27/28] Get ready to deploy Supervisor in CI and run its integration tests - Also use ./test/integration instead of ./test/... everywhere because it will stream the output of the tests while they run --- hack/prepare-for-integration-tests.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index 04aa55317..2037bb41d 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -119,7 +119,7 @@ if ! tilt_mode; then log_note "Checking for running kind clusters..." if ! kind get clusters | grep -q -e '^pinniped$'; then log_note "Creating a kind cluster..." - # single-node.yaml exposes node port 31234 as localhost:12345 + # single-node.yaml exposes node port 31234 as 127.0.0.1:12345 kind create cluster --config "$pinniped_path/hack/lib/kind-config/single-node.yaml" --name pinniped else if ! kubectl cluster-info | grep master | grep -q 127.0.0.1; then @@ -210,8 +210,8 @@ if ! tilt_mode; then --data-value "app_name=$supervisor_app_name" \ --data-value "namespace=$supervisor_namespace" \ --data-value "image_repo=$registry_repo" \ - --data-value-yaml 'service_nodeport_port=31234' \ - --data-value "image_tag=$tag" >"$manifest" + --data-value "image_tag=$tag" \ + --data-value-yaml 'service_nodeport_port=31234' >"$manifest" kapp deploy --yes --app "$supervisor_app_name" --diff-changes --file "$manifest" @@ -250,7 +250,7 @@ kind_capabilities_file="$pinniped_path/test/cluster_capabilities/kind.yaml" pinniped_cluster_capability_file_content=$(cat "$kind_capabilities_file") cat </tmp/integration-test-env -# The following env vars should be set before running 'go test -v -count 1 ./test/...' +# The following env vars should be set before running 'go test -v -count 1 ./test/integration' export PINNIPED_TEST_CONCIERGE_NAMESPACE=${concierge_namespace} export PINNIPED_TEST_CONCIERGE_APP_NAME=${concierge_app_name} export PINNIPED_TEST_USER_USERNAME=${test_username} @@ -260,7 +260,7 @@ export PINNIPED_TEST_WEBHOOK_ENDPOINT=${webhook_url} export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle} export PINNIPED_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace} export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name} -export PINNIPED_TEST_SUPERVISOR_ADDRESS="localhost:12345" +export PINNIPED_TEST_SUPERVISOR_ADDRESS="127.0.0.1:12345" read -r -d '' PINNIPED_TEST_CLUSTER_CAPABILITY_YAML << PINNIPED_TEST_CLUSTER_CAPABILITY_YAML_EOF || true ${pinniped_cluster_capability_file_content} From 6b135b93cf2e8dd374b223c36a2cc89304e47678 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Fri, 9 Oct 2020 18:42:15 -0700 Subject: [PATCH 28/28] Binding both kind workers to the same localhost port fails, so just bind one --- hack/lib/kind-config/multi-node.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/hack/lib/kind-config/multi-node.yaml b/hack/lib/kind-config/multi-node.yaml index 4e4105f69..b9bdaf790 100644 --- a/hack/lib/kind-config/multi-node.yaml +++ b/hack/lib/kind-config/multi-node.yaml @@ -3,6 +3,5 @@ apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker - extraPortMappings: [{containerPort: 31234, hostPort: 12345, protocol: TCP}] - role: worker extraPortMappings: [{containerPort: 31234, hostPort: 12345, protocol: TCP}]