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 d95684d3..b5dc23a1 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 00000000..c15ef7a1 --- /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 00000000..0a1c3106 --- /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 00000000..cd4079c8 --- /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 00000000..ecba850b --- /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 00000000..6df6efe9 --- /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 8340f2e4..05f94288 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 00000000..10319a17 --- /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 c15ef7a1..d6fa97ac 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 10319a17..d1aa8e71 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 00000000..a65f8c8d --- /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 00000000..b89aab97 --- /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 00000000..24825860 --- /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 00000000..795c3d38 --- /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 cd4079c8..bb2f0956 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 6df6efe9..ec0430a9 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 05f94288..f5d5073f 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 b5dc23a1..be8fb170 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 5741a696..8ca7bf44 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 98d2c7bd..5dbcd9b1 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 63456a79..23ad1258 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 c5911c35..790e493e 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 731f97fe..1e337262 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 277e7f3d..75b95719 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 38528fca..89b74704 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 00000000..7000aead --- /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 00000000..961da253 --- /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 0c2458df..1953af02 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 00000000..7000aead --- /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 4e72d35d..f45d80c3 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 09c08925..c678bbfe 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 38be6941..b475f6c6 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 00000000..6cd7dc42 --- /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 61507202..5da5f449 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 00000000..23f8760f --- /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 1722f747..7a1f98a1 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 00000000..68fa7cd6 --- /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 01e9dfa3..c3d7c11f 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 68c725a9..7f0b5b9d 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 00000000..5a6f8faa --- /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 83e7fb87..55aecb0a 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 00000000..961da253 --- /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 faf0ca70..82976801 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 00000000..7000aead --- /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 4e72d35d..f45d80c3 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 29650a3b..717ee6e9 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 665bfd36..dd2faeeb 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 00000000..fc430580 --- /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 61507202..5da5f449 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 00000000..99042aa3 --- /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 71b872b1..47901245 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 00000000..8861fcfa --- /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 bba1a47a..fa7671c9 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 68c725a9..7f0b5b9d 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 00000000..d567208c --- /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 ac54aea2..76c671b6 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 00000000..961da253 --- /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 9e0a6579..6de47016 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 00000000..7000aead --- /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 4e72d35d..f45d80c3 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 04fa22e5..0701323f 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 5c1235c5..8edfd136 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 00000000..8e01a067 --- /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 61507202..5da5f449 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 00000000..7b32bf1e --- /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 65e3d109..16d51235 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 00000000..1dfdfd82 --- /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 6a761df3..b80a9fec 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 68c725a9..7f0b5b9d 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 00000000..7d07e330 --- /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 c27ff8c0..95454e7b 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 00000000..961da253 --- /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 00000000..80b6c519 --- /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 bb2f0956..cd4079c8 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 ec0430a9..6df6efe9 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 f5d5073f..05f94288 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 00000000..4e4105f6 --- /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 00000000..d62f3a59 --- /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 05f94288..ba39ad4f 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 d5eec031..b1ff45a9 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 80b6c519..6c5cce78 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 c3479a37..257bd321 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 18936d7e..7cd4b75d 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 18936d7e..7cd4b75d 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 18936d7e..7cd4b75d 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 18936d7e..7cd4b75d 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 ba39ad4f..3a1a04fa 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 d6fa97ac..6e086633 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 ecba850b..a4b34e9a 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 d1aa8e71..a060e4e9 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 a65f8c8d..96f858f5 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 b89aab97..47fab070 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 24825860..36ec8c59 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 00000000..356b338b --- /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 6c5cce78..d0456ce0 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 6e086633..96758ba2 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 96f858f5..2293e963 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 47fab070..7d1c481d 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 d0456ce0..d2e0bca2 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 a060e4e9..085475b1 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 7d1c481d..49f46beb 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 356b338b..128502f4 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 b1ff45a9..b1c28cf6 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 96758ba2..9b111f50 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 085475b1..00000000 --- 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 00000000..204600ab --- /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 2293e963..84ca1c77 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 49f46beb..14f2d9b6 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 36ec8c59..00000000 --- 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 00000000..a50a7095 --- /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 00000000..c7c24de4 --- /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 128502f4..2da0e3a8 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 d2e0bca2..148e1878 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 7000aead..a2850bdf 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 9b111f50..dbbf5cfc 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 a4b34e9a..bfa4ac95 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 961da253..c1cb5525 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 1953af02..716d1b03 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 7000aead..75931e70 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 f45d80c3..253a5c5b 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 6cd7dc42..a0a85394 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 23f8760f..b080830d 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 55aecb0a..d25d0c88 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 961da253..c1cb5525 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 82976801..6eef0246 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 7000aead..75931e70 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 f45d80c3..253a5c5b 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 fc430580..a2c2db1b 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 99042aa3..c1abf9ed 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 76c671b6..eb0e2dfa 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 961da253..c1cb5525 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 6de47016..3008fb69 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 7000aead..75931e70 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 f45d80c3..253a5c5b 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 8e01a067..5011336e 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 7b32bf1e..42765a4c 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 95454e7b..c7ed5084 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 961da253..c1cb5525 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 204600ab..cd827c94 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 148e1878..94f3cd9d 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 dbbf5cfc..fe04cd7f 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 cd827c94..3f246a85 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 00000000..f9d9f2f8 --- /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 84ca1c77..c919e150 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 14f2d9b6..e21b3c4a 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 795c3d38..d78f199c 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 a50a7095..0c11b5be 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 c7c24de4..cc427167 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 2da0e3a8..81204e28 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 94f3cd9d..77dcaebf 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 0c11b5be..0626c48c 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 00000000..c877f3ac --- /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 00000000..6b76280b --- /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 f9d9f2f8..c9eab6b9 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 00000000..cb96771e --- /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 3f246a85..3f21d5fe 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 6b76280b..a7f549a8 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 c9eab6b9..5f87107f 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 a2850bdf..a05f4339 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 c1cb5525..062dca9f 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 716d1b03..c74ee743 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 75931e70..a05f4339 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 253a5c5b..262992cb 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 d25d0c88..8e5ad5b8 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 c1cb5525..062dca9f 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 6eef0246..eaa1e5bf 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 75931e70..a05f4339 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 253a5c5b..262992cb 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 eb0e2dfa..75393f9a 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 c1cb5525..062dca9f 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 3008fb69..1dc10f4c 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 75931e70..a05f4339 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 253a5c5b..262992cb 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 c7ed5084..277a97cb 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 c1cb5525..062dca9f 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 3f21d5fe..d6420f4c 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 a7f549a8..7db8022d 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 9f5cb13d..aecbeb0e 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 d1d2f404..44334af9 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 0a1c3106..7920b6cb 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 52d9e766..6318988c 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 0a7372ef..16b77077 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 7985140a..0e2cd87f 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 e6df6b94..98593f03 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 0e2cd87f..f792ce24 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 1e337262..c1b13c74 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 ebf1776b..a5dba407 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 c291ca70..42fb8d75 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 16b338e6..a4705513 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 a5323b6f..5616b09d 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 75b95719..e1a57a0a 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 4dfb7121..6549fa4d 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 a208f5bc..a5cdcdb3 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 257bd321..f97aa5f8 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 29e4d7ce..553a8745 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 0a25f7ee..49271f0f 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 00000000..7c96dca8 --- /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 00000000..72da2d6d --- /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 16b77077..be2c8792 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 8bbc4d13..3ddf4033 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 00000000..ddad9325 --- /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 00000000..22d7e204 --- /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 f792ce24..bf7c299d 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 acbf5a3f..85a7b8e4 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 c641c515..8f9dd202 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 42fb8d75..94f40e12 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 cd4079c8..459276fb 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 00000000..6acbe7fc --- /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 6df6efe9..6e74ca36 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 be2c8792..26213238 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 ddad9325..00000000 --- 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 bf7c299d..04aa5531 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 7920b6cb..87db1b9c 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 04aa5531..2037bb41 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 4e4105f6..b9bdaf79 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}]