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/Dockerfile b/Dockerfile index d95684d3..be8fb170 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,18 +20,20 @@ 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/... # Use a runtime image based on Debian slim 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 # Document the port EXPOSE 443 # Set the entrypoint -ENTRYPOINT ["/usr/local/bin/pinniped-server"] +ENTRYPOINT ["/usr/local/bin/pinniped-concierge"] 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/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..a05f4339 --- /dev/null +++ b/apis/config/v1alpha1/types_oidcproviderconfig.go.tmpl @@ -0,0 +1,72 @@ +// 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" + +// +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 + // 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"` +} + +// 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"` + + // 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. +// +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:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` +} + +// 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/cmd/pinniped-server/main.go b/cmd/pinniped-concierge/main.go similarity index 95% rename from cmd/pinniped-server/main.go rename to cmd/pinniped-concierge/main.go index 42293796..e6fdf991 100644 --- a/cmd/pinniped-server/main.go +++ b/cmd/pinniped-concierge/main.go @@ -13,7 +13,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/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go new file mode 100644 index 00000000..fe04cd7f --- /dev/null +++ b/cmd/pinniped-supervisor/main.go @@ -0,0 +1,152 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "net" + "net/http" + "os" + "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" + "k8s.io/component-base/logs" + "k8s.io/klog/v2" + + 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" + "go.pinniped.dev/internal/oidc/provider/manager" +) + +const ( + singletonWorker = 1 + defaultResyncInterval = 3 * time.Minute +) + +func start(ctx context.Context, l net.Listener, handler http.Handler) { + server := http.Server{Handler: handler} + + 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) + } + } + }() +} + +func waitForSignal() os.Signal { + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, os.Interrupt) + return <-signalCh +} + +func startControllers( + ctx context.Context, + issuerProvider *manager.Manager, + pinnipedClient pinnipedclientset.Interface, + pinnipedInformers pinnipedinformers.SharedInformerFactory, +) { + // Create controller manager. + controllerManager := controllerlib. + NewManager(). + WithController( + supervisorconfig.NewOIDCProviderConfigWatcherController( + issuerProvider, + clock.RealClock{}, + pinnipedClient, + pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(), + controllerlib.WithInformer, + ), + singletonWorker, + ) + + pinnipedInformers.Start(ctx.Done()) + + go controllerManager.Start(ctx) +} + +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. + pinnipedClient, err := pinnipedclientset.NewForConfig(kubeConfig) + if err != nil { + return nil, fmt.Errorf("could not load in-cluster configuration: %w", err) + } + + return pinnipedClient, nil +} + +func run(serverInstallationNamespace string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + pinnipedClient, err := newPinnipedClient() + if err != nil { + return fmt.Errorf("cannot create k8s client: %w", err) + } + + pinnipedInformers := pinnipedinformers.NewSharedInformerFactoryWithOptions( + pinnipedClient, + defaultResyncInterval, + pinnipedinformers.WithNamespace(serverInstallationNamespace), + ) + + oidProvidersManager := manager.NewManager(http.NotFoundHandler()) + startControllers(ctx, oidProvidersManager, pinnipedClient, pinnipedInformers) + + //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() + + start(ctx, l, oidProvidersManager) + klog.InfoS("supervisor is ready", "address", l.Addr().String()) + + gotSignal := waitForSignal() + klog.InfoS("supervisor exiting", "signal", gotSignal) + + return nil +} + +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)) + } + + if err := run(podInfo.Namespace); err != nil { + klog.Fatal(err) + } +} 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 96% rename from deploy/values.yaml rename to deploy/concierge/values.yaml index 0a25f7ee..49271f0f 100644 --- a/deploy/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/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 new file mode 100644 index 00000000..87db1b9c --- /dev/null +++ b/deploy/supervisor/README.md @@ -0,0 +1,110 @@ +# 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` 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 + +### 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 +``` diff --git a/deploy/supervisor/config.pinniped.dev_oidcproviderconfigs.yaml b/deploy/supervisor/config.pinniped.dev_oidcproviderconfigs.yaml new file mode 100644 index 00000000..062dca9f --- /dev/null +++ b/deploy/supervisor/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,86 @@ + +--- +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 + spec: + 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 + 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 + OIDC Provider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object + required: + - spec + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/deploy/supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml new file mode 100644 index 00000000..459276fb --- /dev/null +++ b/deploy/supervisor/deployment.yaml @@ -0,0 +1,112 @@ +#! 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 + - /etc/config/pinniped.yaml + resources: + requests: + memory: "128Mi" + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: podinfo + mountPath: /etc/podinfo + 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 diff --git a/deploy/supervisor/rbac.yaml b/deploy/supervisor/rbac.yaml new file mode 100644 index 00000000..bfa4ac95 --- /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: [config.pinniped.dev] + resources: [oidcproviderconfigs] + verbs: [update, 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/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 new file mode 100644 index 00000000..6e74ca36 --- /dev/null +++ b/deploy/supervisor/values.yaml @@ -0,0 +1,29 @@ +#! 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"}}} + +#! 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/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/generated/1.17/README.adoc b/generated/1.17/README.adoc index 0c2458df..c74ee743 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -95,6 +95,65 @@ 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`. + +| *`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. +|=== + + + + +[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}-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 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). +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 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.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..a05f4339 --- /dev/null +++ b/generated/1.17/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -0,0 +1,72 @@ +// 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" + +// +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 + // 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"` +} + +// 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"` + + // 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. +// +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:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` +} + +// 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..262992cb 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,100 @@ 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 + in.Status.DeepCopyInto(&out.Status) + 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 +} + +// 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 +} + +// 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/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..a0a85394 --- /dev/null +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -0,0 +1,127 @@ +// 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 +} + +// 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. + 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..b080830d --- /dev/null +++ b/generated/1.17/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,178 @@ +// 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) + 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) + 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 +} + +// 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(). + 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..8e5ad5b8 100644 --- a/generated/1.17/client/openapi/zz_generated.openapi.go +++ b/generated/1.17/client/openapi/zz_generated.openapi.go @@ -22,6 +22,10 @@ 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/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), @@ -286,6 +290,155 @@ 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"), + }, + }, + "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{"spec"}, + }, + }, + Dependencies: []string{ + "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"}, + } +} + +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_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 OIDC Provider. 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: "", + }, + }, + "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"}, + } +} + 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..062dca9f --- /dev/null +++ b/generated/1.17/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,86 @@ + +--- +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 + spec: + 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 + 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 + OIDC Provider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object + required: + - spec + 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..eaa1e5bf 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -95,6 +95,65 @@ 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`. + +| *`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. +|=== + + + + +[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}-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 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). +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 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.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..a05f4339 --- /dev/null +++ b/generated/1.18/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -0,0 +1,72 @@ +// 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" + +// +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 + // 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"` +} + +// 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"` + + // 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. +// +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:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` +} + +// 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..262992cb 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,100 @@ 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 + in.Status.DeepCopyInto(&out.Status) + 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 +} + +// 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 +} + +// 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/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..a2c2db1b --- /dev/null +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -0,0 +1,129 @@ +// 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 +} + +// 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. + 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..c1abf9ed --- /dev/null +++ b/generated/1.18/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,182 @@ +// 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) + 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) + 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 +} + +// 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(). + 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..75393f9a 100644 --- a/generated/1.18/client/openapi/zz_generated.openapi.go +++ b/generated/1.18/client/openapi/zz_generated.openapi.go @@ -22,6 +22,10 @@ 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/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), @@ -286,6 +290,155 @@ 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"), + }, + }, + "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{"spec"}, + }, + }, + Dependencies: []string{ + "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"}, + } +} + +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_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 OIDC Provider. 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: "", + }, + }, + "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"}, + } +} + 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..062dca9f --- /dev/null +++ b/generated/1.18/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,86 @@ + +--- +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 + spec: + 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 + 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 + OIDC Provider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object + required: + - spec + 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..1dc10f4c 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -95,6 +95,65 @@ 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`. + +| *`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. +|=== + + + + +[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}-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 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). +|=== + + [id="{anchor_prefix}-idp-pinniped-dev-v1alpha1"] === idp.pinniped.dev/v1alpha1 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 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..a05f4339 --- /dev/null +++ b/generated/1.19/apis/config/v1alpha1/types_oidcproviderconfig.go @@ -0,0 +1,72 @@ +// 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" + +// +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 + // 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"` +} + +// 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"` + + // 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. +// +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:"spec"` + + // Status of the OIDC provider. + Status OIDCProviderConfigStatus `json:"status,omitempty"` +} + +// 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..262992cb 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,100 @@ 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 + in.Status.DeepCopyInto(&out.Status) + 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 +} + +// 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 +} + +// 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/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..5011336e --- /dev/null +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/fake/fake_oidcproviderconfig.go @@ -0,0 +1,129 @@ +// 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 +} + +// 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. + 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..42765a4c --- /dev/null +++ b/generated/1.19/client/clientset/versioned/typed/config/v1alpha1/oidcproviderconfig.go @@ -0,0 +1,182 @@ +// 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) + 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) + 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 +} + +// 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(). + 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..277a97cb 100644 --- a/generated/1.19/client/openapi/zz_generated.openapi.go +++ b/generated/1.19/client/openapi/zz_generated.openapi.go @@ -22,6 +22,10 @@ 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/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), @@ -287,6 +291,155 @@ 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"), + }, + }, + "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{"spec"}, + }, + }, + Dependencies: []string{ + "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"}, + } +} + +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_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 OIDC Provider. 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: "", + }, + }, + "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"}, + } +} + 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..062dca9f --- /dev/null +++ b/generated/1.19/crds/config.pinniped.dev_oidcproviderconfigs.yaml @@ -0,0 +1,86 @@ + +--- +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 + spec: + 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 + 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 + OIDC Provider. Note that this Status can represent success or failure. + enum: + - Success + - Duplicate + - Invalid + type: string + type: object + required: + - spec + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] 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/kind-config/multi-node.yaml b/hack/lib/kind-config/multi-node.yaml new file mode 100644 index 00000000..b9bdaf79 --- /dev/null +++ b/hack/lib/kind-config/multi-node.yaml @@ -0,0 +1,7 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + - role: worker + - 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/lib/tilt/Tiltfile b/hack/lib/tilt/Tiltfile index 0a7372ef..26213238 100644 --- a/hack/lib/tilt/Tiltfile +++ b/hack/lib/tilt/Tiltfile @@ -8,13 +8,20 @@ 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 +# + # 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', @@ -26,16 +33,17 @@ 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', ])) # 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', @@ -43,55 +51,105 @@ k8s_resource( ], ) -# 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', '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', 'service_nodeport_port=31234', +])) + +# Collect all the deployed supervisor resources under a "supervisor" resource tab. +k8s_resource( + 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', + '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'], +) + +##################################################################################################### +# Concierge app +# + +# Render the Concierge server installation manifest using ytt. k8s_yaml(local([ 'sh', '-c', - 'ytt --file ../../../deploy ' + - '--data-value namespace=integration ' + - '--data-value image_repo=image/pinniped ' + + 'ytt --file ../../../deploy/concierge ' + + '--data-value app_name=pinniped-concierge ' + + '--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 "deploy/pinniped" resource tab. +# Collect all the deployed local-user-authenticator resources under a "concierge" resource tab. k8s_resource( - workload='pinniped', + 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', + '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/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 e4f3bd0f..2037bb41 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -50,6 +50,7 @@ function check_dependency() { # help=no skip_build=no +clean_kind=no while (("$#")); do case "$1" in @@ -61,6 +62,10 @@ while (("$#")); do skip_build=yes shift ;; + -c | --clean) + clean_kind=yes + shift + ;; -*) log_error "Unsupported flag $1" >&2 exit 1 @@ -98,18 +103,24 @@ 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 ! tilt_mode; then + if [[ "$clean_kind" == "yes" ]]; then + log_note "Deleting running kind clusters to prepare from a clean slate..." + 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..." - kind create cluster + # 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 log_error "Seems like your kubeconfig is not targeting a local cluster." @@ -146,14 +157,14 @@ 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 # # 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 . \ @@ -167,7 +178,6 @@ if ! tilt_mode; then fi - test_username="test-username" test_groups="test-group-0,test-group-1" set +o pipefail @@ -186,27 +196,49 @@ kubectl create secret generic "$test_username" \ --output yaml | kubectl apply -f - -app_name="pinniped" -namespace="integration" +# +# Deploy the Pinniped Supervisor +# +supervisor_app_name="pinniped-supervisor" +supervisor_namespace="supervisor" + +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" \ + --data-value-yaml 'service_nodeport_port=31234' >"$manifest" + + kapp deploy --yes --app "$supervisor_app_name" --diff-changes --file "$manifest" + + popd >/dev/null +fi + +# +# Deploy Pinniped +# +concierge_app_name="pinniped-concierge" +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}')" 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 . \ - --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 @@ -218,20 +250,23 @@ 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/...' -export PINNIPED_NAMESPACE=${namespace} -export PINNIPED_APP_NAME=${app_name} +# 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} 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_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace} +export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name} +export PINNIPED_TEST_SUPERVISOR_ADDRESS="127.0.0.1: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 # @@ -242,15 +277,16 @@ 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}" +log_note " ${goland_vars}PINNIPED_TEST_CLUSTER_CAPABILITY_FILE=${kind_capabilities_file}" 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/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 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/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go new file mode 100644 index 00000000..d6420f4c --- /dev/null +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher.go @@ -0,0 +1,161 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package supervisorconfig + +import ( + "context" + "fmt" + + "go.pinniped.dev/internal/multierror" + + 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" + "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 + 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( + providerSetter ProvidersSetter, + clock clock.Clock, + client pinnipedclientset.Interface, + opcInformer configinformers.OIDCProviderConfigInformer, + withInformer pinnipedcontroller.WithInformerOptionFunc, +) controllerlib.Controller { + return controllerlib.New( + controllerlib.Config{ + Name: "OIDCProviderConfigWatcherController", + Syncer: &oidcProviderConfigWatcherController{ + providerSetter: providerSetter, + clock: clock, + client: client, + 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 + } + + issuerCounts := make(map[string]int) + for _, opc := range all { + issuerCounts[opc.Spec.Issuer]++ + } + + errs := multierror.New() + + oidcProviders := make([]*provider.OIDCProvider, 0) + for _, opc := range all { + if issuerCount := issuerCounts[opc.Spec.Issuer]; issuerCount > 1 { + if err := c.updateStatus( + ctx.Context, + opc.Namespace, + opc.Name, + configv1alpha1.DuplicateOIDCProviderStatus, + "Duplicate issuer: "+opc.Spec.Issuer, + ); err != nil { + errs.Add(fmt.Errorf("could not update status: %w", err)) + } + continue + } + + oidcProvider, err := provider.NewOIDCProvider(opc.Spec.Issuer) + if err != nil { + if err := c.updateStatus( + ctx.Context, + opc.Namespace, + opc.Name, + configv1alpha1.InvalidOIDCProviderStatus, + "Invalid: "+err.Error(), + ); err != nil { + errs.Add(fmt.Errorf("could not update status: %w", err)) + } + continue + } + + 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)) + continue + } + oidcProviders = append(oidcProviders, oidcProvider) + } + + 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 + 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 new file mode 100644 index 00000000..7db8022d --- /dev/null +++ b/internal/controller/supervisorconfig/oidcproviderconfig_watcher_test.go @@ -0,0 +1,818 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +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" +) + +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) { + const namespace = "some-namespace" + + 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 + 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. + 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: 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() + + oidcProviderConfigGVR = schema.GroupVersionResource{ + Group: v1alpha1.SchemeGroupVersion.Group, + Version: v1alpha1.SchemeGroupVersion.Version, + Resource: "oidcproviderconfigs", + } + }) + + it.After(func() { + timeoutContextCancel() + }) + + 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: 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: namespace}, + Spec: v1alpha1.OIDCProviderConfigSpec{Issuer: "https://issuer2.com"}, + } + r.NoError(pinnipedAPIClient.Tracker().Add(oidcProviderConfig2)) + r.NoError(opcInformerClient.Tracker().Add(oidcProviderConfig2)) + }) + + it("calls the ProvidersSetter", 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, + ) + }) + + 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" + 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( + 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" + 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)) + }) + + 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" + oidcProviderConfig2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + + 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" + 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( + 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() { + 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" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + + 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" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + + 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" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + + 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() { + 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" + 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( + 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" + 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( + 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("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" + 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( + 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" + oidcProviderConfig.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + + 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() { + 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{})) +} diff --git a/internal/multierror/multierror.go b/internal/multierror/multierror.go new file mode 100644 index 00000000..5f87107f --- /dev/null +++ b/internal/multierror/multierror.go @@ -0,0 +1,63 @@ +// 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 ( + "fmt" + "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. +type MultiError []error + +// New returns an empty MultiError. +func New() MultiError { + return make([]error, 0) +} + +// Add adds an error to the MultiError. The provided err must not be nil. +func (m *MultiError) Add(err error) { + *m = append(*m, err) +} + +// Error implements the error.Error() interface method. +func (m MultiError) Error() string { + sb := strings.Builder{} + formatFunc(m, &sb) + return sb.String() +} + +// 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 +} + +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()) + } +} 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") +} diff --git a/internal/oidc/discovery/discovery.go b/internal/oidc/discovery/discovery.go new file mode 100644 index 00000000..c919e150 --- /dev/null +++ b/internal/oidc/discovery/discovery.go @@ -0,0 +1,69 @@ +// 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" + "net/http" + + "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 { + // vvv Required vvv + + Issuer string `json:"issuer"` + + AuthorizationEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + 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 ^^^ + + // 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 ^^^ +} + +// 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") + + if r.Method != http.MethodGet { + http.Error(w, `{"error": "Method not allowed (try GET)"}`, http.StatusMethodNotAllowed) + return + } + + oidcConfig := Metadata{ + Issuer: issuerURL, + AuthorizationEndpoint: issuerURL + oidc.AuthorizationEndpointPath, + TokenEndpoint: issuerURL + oidc.TokenEndpointPath, + JWKSURI: issuerURL + oidc.JWKSEndpointPath, + 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 new file mode 100644 index 00000000..e21b3c4a --- /dev/null +++ b/internal/oidc/discovery/discovery_test.go @@ -0,0 +1,82 @@ +// 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" +) + +func TestDiscovery(t *testing.T) { + tests := []struct { + name string + + issuer string + method string + path string + + wantStatus int + wantContentType string + wantBody interface{} + }{ + { + name: "happy path", + issuer: "https://some-issuer.com/some/path", + method: http.MethodGet, + 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/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"}, + IDTokenSigningAlgValuesSupported: []string{"RS256"}, + TokenEndpointAuthMethodsSupported: []string{"client_secret_basic"}, + TokenEndpointAuthSigningAlgoValuesSupported: []string{"RS256"}, + ScopesSupported: []string{"openid", "offline"}, + ClaimsSupported: []string{"groups"}, + }, + }, + { + name: "bad method", + issuer: "https://some-issuer.com", + method: http.MethodPost, + path: oidc.WellKnownEndpointPath, + 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) { + handler := New(test.issuer) + req := httptest.NewRequest(test.method, test.path, 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/oidc.go b/internal/oidc/oidc.go new file mode 100644 index 00000000..d78f199c --- /dev/null +++ b/internal/oidc/oidc.go @@ -0,0 +1,12 @@ +// 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 ( + 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/manager.go b/internal/oidc/provider/manager/manager.go new file mode 100644 index 00000000..0626c48c --- /dev/null +++ b/internal/oidc/provider/manager/manager.go @@ -0,0 +1,77 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package manager + +import ( + "net/http" + "sync" + + "k8s.io/klog/v2" + + "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. +// +// It is thread-safe. +type Manager struct { + mu sync.RWMutex + 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]http.Handler), nextHandler: nextHandler} +} + +// 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 (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 { + 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 (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 + } + requestHandler.ServeHTTP(resp, req) +} + +func (m *Manager) findHandler(req *http.Request) http.Handler { + m.mu.RLock() + defer m.mu.RUnlock() + + 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) + }) + }) + }) +} diff --git a/internal/oidc/provider/oidcprovider.go b/internal/oidc/provider/oidcprovider.go new file mode 100644 index 00000000..cc427167 --- /dev/null +++ b/internal/oidc/provider/oidcprovider.go @@ -0,0 +1,80 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package provider + +import ( + "fmt" + "net/url" + "strings" + + "go.pinniped.dev/internal/constable" +) + +// OIDCProvider represents all of the settings and state for an OIDC provider. +type OIDCProvider struct { + issuer string + issuerHost string + issuerPath string +} + +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") + } + + 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 issuerURL.User != nil { + return constable.Error(`issuer must not have username or password`) + } + + if strings.HasSuffix(issuerURL.Path, "/") { + return constable.Error(`issuer must not have trailing slash in path`) + } + + if issuerURL.RawQuery != "" { + return constable.Error(`issuer must not have query`) + } + + 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 new file mode 100644 index 00000000..81204e28 --- /dev/null +++ b/internal/oidc/provider/oidcprovider_test.go @@ -0,0 +1,78 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package provider + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOIDCProviderValidations(t *testing.T) { + tests := []struct { + name string + issuer string + wantError string + }{ + { + name: "provider must have an issuer", + issuer: "", + wantError: "provider must have an issuer", + }, + { + name: "no scheme", + issuer: "tuna.com", + wantError: `issuer must have "https" scheme`, + }, + { + name: "bad scheme", + issuer: "ftp://tuna.com", + wantError: `issuer must have "https" scheme`, + }, + { + name: "fragment", + issuer: "https://tuna.com/fish#some-frag", + wantError: `issuer must not have fragment`, + }, + { + name: "query", + issuer: "https://tuna.com?some=query", + wantError: `issuer must not have query`, + }, + { + name: "username", + issuer: "https://username@tuna.com", + wantError: `issuer must not have username or password`, + }, + { + name: "password", + issuer: "https://username:password@tuna.com", + wantError: `issuer must not have username or password`, + }, + { + name: "without path", + issuer: "https://tuna.com", + }, + { + name: "with path", + issuer: "https://tuna.com/fish/marlin", + }, + { + name: "trailing slash in path", + 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) { + _, err := NewOIDCProvider(tt.issuer) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + } else { + require.NoError(t, err) + } + }) + } +} 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/cli_test.go b/test/integration/cli_test.go index 731f97fe..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", - 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", - accessAsGroupWithKubectlTest(ctx, adminClient, kubeConfigYAML, group, env.Namespace), + library.AccessAsGroupWithKubectlTest(ctx, adminClient, kubeConfigYAML, group, env.ConciergeNamespace), ) } @@ -76,10 +51,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/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/api_serving_certs_test.go b/test/integration/concierge_api_serving_certs_test.go similarity index 91% rename from test/integration/api_serving_certs_test.go rename to test/integration/concierge_api_serving_certs_test.go index c291ca70..94f40e12 100644 --- a/test/integration/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 @@ -83,7 +82,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 +97,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 +132,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/app_availability_test.go b/test/integration/concierge_availability_test.go similarity index 90% rename from test/integration/app_availability_test.go rename to test/integration/concierge_availability_test.go index 16b338e6..a4705513 100644 --- a/test/integration/app_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/credentialissuerconfig_test.go b/test/integration/concierge_credentialissuerconfig_test.go similarity index 98% rename from test/integration/credentialissuerconfig_test.go rename to test/integration/concierge_credentialissuerconfig_test.go index a5323b6f..5616b09d 100644 --- a/test/integration/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/credentialrequest_test.go b/test/integration/concierge_credentialrequest_test.go similarity index 93% rename from test/integration/credentialrequest_test.go rename to test/integration/concierge_credentialrequest_test.go index 277e7f3d..e1a57a0a 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), ) } } @@ -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/kubecertagent_test.go b/test/integration/concierge_kubecertagent_test.go similarity index 90% rename from test/integration/kubecertagent_test.go rename to test/integration/concierge_kubecertagent_test.go index 4dfb7121..6549fa4d 100644 --- a/test/integration/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/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/api_discovery_test.go b/test/integration/kube_api_discovery_test.go similarity index 90% rename from test/integration/api_discovery_test.go rename to test/integration/kube_api_discovery_test.go index d5eec031..b1c28cf6 100644 --- a/test/integration/api_discovery_test.go +++ b/test/integration/kube_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"}, + }, }, }, }, @@ -131,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") } }) } diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go new file mode 100644 index 00000000..77dcaebf --- /dev/null +++ b/test/integration/supervisor_discovery_test.go @@ -0,0 +1,254 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "testing" + "time" + + "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" +) + +func TestSupervisorOIDCDiscovery(t *testing.T) { + env := library.IntegrationEnv(t) + client := library.NewPinnipedClientset(t) + + ns := env.SupervisorNamespace + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + // 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) + } + + // 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 + 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) + } + }) + + // 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) + 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. + 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. + 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, 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) { + 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("%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) + require.Equal(t, http.StatusNotFound, response.StatusCode) + err = response.Body.Close() + require.NoError(t, err) +} + +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) + requireStatus(t, client, ns, oidcProviderConfigName, v1alpha1.SuccessOIDCProviderStatus) + 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) + + // 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) +} + +func requireWellKnownEndpointIsWorking(t *testing.T, issuerName string) { + t.Helper() + httpClient := &http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + // 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("%s/.well-known/openid-configuration", issuerName), + 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 + 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() + require.NoError(t, err) + + // Check that the response matches our expectations. + expectedResultTemplate := here.Doc(`{ + "issuer": "%s", + "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", + "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, 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 +} + +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) +} 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, 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 c3479a37..f97aa5f8 100644 --- a/test/library/env.go +++ b/test/library/env.go @@ -25,11 +25,15 @@ 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 { + ConciergeNamespace string `json:"conciergeNamespace"` + SupervisorNamespace string `json:"supervisorNamespace"` + 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 { Token string `json:"token"` ExpectedUsername string `json:"expectedUsername"` ExpectedGroups []string `json:"expectedGroups"` @@ -42,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) @@ -65,12 +69,15 @@ 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_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 return &result