Merge pull request #158 from vmware-tanzu/label_every_resource
Custom labels can to be applied to all k8s resources created by Pinniped
This commit is contained in:
commit
08659a6583
@ -14,7 +14,6 @@ RUN go mod download
|
||||
COPY generated ./generated
|
||||
COPY cmd ./cmd
|
||||
COPY internal ./internal
|
||||
COPY pkg ./pkg
|
||||
COPY tools ./tools
|
||||
COPY hack ./hack
|
||||
|
||||
|
@ -310,6 +310,9 @@ func startControllers(
|
||||
apicerts.NewCertsManagerController(
|
||||
namespace,
|
||||
certsSecretResourceName,
|
||||
map[string]string{
|
||||
"app": "local-user-authenticator",
|
||||
},
|
||||
kubeClient,
|
||||
kubeInformers.Core().V1().Secrets(),
|
||||
controllerlib.WithInformer,
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned"
|
||||
pinnipedinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions"
|
||||
"go.pinniped.dev/internal/config/supervisor"
|
||||
"go.pinniped.dev/internal/controller/supervisorconfig"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/downward"
|
||||
@ -63,6 +64,7 @@ func waitForSignal() os.Signal {
|
||||
|
||||
func startControllers(
|
||||
ctx context.Context,
|
||||
cfg *supervisor.Config,
|
||||
issuerProvider *manager.Manager,
|
||||
kubeClient kubernetes.Interface,
|
||||
pinnipedClient pinnipedclientset.Interface,
|
||||
@ -84,6 +86,7 @@ func startControllers(
|
||||
).
|
||||
WithController(
|
||||
supervisorconfig.NewJWKSController(
|
||||
cfg.Labels,
|
||||
kubeClient,
|
||||
pinnipedClient,
|
||||
kubeInformers.Core().V1().Secrets(),
|
||||
@ -120,7 +123,7 @@ func newClients() (kubernetes.Interface, pinnipedclientset.Interface, error) {
|
||||
return kubeClient, pinnipedClient, nil
|
||||
}
|
||||
|
||||
func run(serverInstallationNamespace string) error {
|
||||
func run(serverInstallationNamespace string, cfg *supervisor.Config) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@ -142,7 +145,7 @@ func run(serverInstallationNamespace string) error {
|
||||
)
|
||||
|
||||
oidProvidersManager := manager.NewManager(http.NotFoundHandler())
|
||||
startControllers(ctx, oidProvidersManager, kubeClient, pinnipedClient, kubeInformers, pinnipedInformers)
|
||||
startControllers(ctx, cfg, oidProvidersManager, kubeClient, pinnipedClient, kubeInformers, pinnipedInformers)
|
||||
|
||||
//nolint: gosec // Intentionally binding to all network interfaces.
|
||||
l, err := net.Listen("tcp", ":80")
|
||||
@ -173,7 +176,13 @@ func main() {
|
||||
klog.Fatal(fmt.Errorf("could not read pod metadata: %w", err))
|
||||
}
|
||||
|
||||
if err := run(podInfo.Namespace); err != nil {
|
||||
// Read the server config file.
|
||||
cfg, err := supervisor.FromPath(os.Args[2])
|
||||
if err != nil {
|
||||
klog.Fatal(fmt.Errorf("could not load config: %w", err))
|
||||
}
|
||||
|
||||
if err := run(podInfo.Namespace, cfg); err != nil {
|
||||
klog.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -2,28 +2,31 @@
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("@ytt:json", "json")
|
||||
#@ load("helpers.lib.yaml", "defaultLabel", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix")
|
||||
|
||||
#@ if not data.values.into_namespace:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: #@ data.values.namespace
|
||||
labels:
|
||||
name: #@ data.values.namespace
|
||||
labels: #@ labels()
|
||||
#@ end
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-config"
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceNameWithSuffix("config")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
data:
|
||||
#! If names.apiService is changed in this ConfigMap, must also change name of the ClusterIP Service resource below.
|
||||
#@yaml/text-templated-strings
|
||||
@ -35,11 +38,12 @@ data:
|
||||
durationSeconds: (@= str(data.values.api_serving_certificate_duration_seconds) @)
|
||||
renewBeforeSeconds: (@= str(data.values.api_serving_certificate_renew_before_seconds) @)
|
||||
names:
|
||||
servingCertificateSecret: (@= data.values.app_name + "-api-tls-serving-certificate" @)
|
||||
credentialIssuerConfig: (@= data.values.app_name + "-config" @)
|
||||
apiService: (@= data.values.app_name + "-api" @)
|
||||
servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @)
|
||||
credentialIssuerConfig: (@= defaultResourceNameWithSuffix("config") @)
|
||||
apiService: (@= defaultResourceNameWithSuffix("api") @)
|
||||
labels: (@= json.encode(labels()).rstrip() @)
|
||||
kubeCertAgent:
|
||||
namePrefix: (@= data.values.app_name + "-kube-cert-agent-" @)
|
||||
namePrefix: (@= defaultResourceNameWithSuffix("kube-cert-agent-") @)
|
||||
(@ if data.values.kube_cert_agent_image: @)
|
||||
image: (@= data.values.kube_cert_agent_image @)
|
||||
(@ else: @)
|
||||
@ -59,9 +63,8 @@ apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: image-pull-secret
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: #@ data.values.image_pull_dockerconfigjson
|
||||
@ -70,29 +73,26 @@ data:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
replicas: #@ data.values.replicas
|
||||
selector:
|
||||
matchLabels:
|
||||
app: #@ data.values.app_name
|
||||
matchLabels: #@ defaultLabel()
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
labels: #@ defaultLabel()
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ""
|
||||
spec:
|
||||
serviceAccountName: #@ data.values.app_name
|
||||
serviceAccountName: #@ defaultResourceName()
|
||||
#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "":
|
||||
imagePullSecrets:
|
||||
- name: image-pull-secret
|
||||
#@ end
|
||||
containers:
|
||||
- name: pinniped
|
||||
- name: #@ defaultResourceName()
|
||||
#@ if data.values.image_digest:
|
||||
image: #@ data.values.image_repo + "@" + data.values.image_digest
|
||||
#@ else:
|
||||
@ -131,7 +131,7 @@ spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: #@ data.values.app_name + "-config"
|
||||
name: #@ defaultResourceNameWithSuffix("config")
|
||||
- name: podinfo
|
||||
downwardAPI:
|
||||
items:
|
||||
@ -157,22 +157,19 @@ spec:
|
||||
- weight: 50
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: #@ data.values.app_name
|
||||
matchLabels: #@ defaultLabel()
|
||||
topologyKey: kubernetes.io/hostname
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
#! If name is changed, must also change names.apiService in the ConfigMap above and spec.service.name in the APIService below.
|
||||
name: #@ data.values.app_name + "-api"
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceNameWithSuffix("api")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: #@ data.values.app_name
|
||||
selector: #@ defaultLabel()
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
@ -182,8 +179,7 @@ apiVersion: apiregistration.k8s.io/v1
|
||||
kind: APIService
|
||||
metadata:
|
||||
name: v1alpha1.login.pinniped.dev
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
version: v1alpha1
|
||||
group: login.pinniped.dev
|
||||
@ -191,6 +187,6 @@ spec:
|
||||
versionPriority: 10
|
||||
#! caBundle: Do not include this key here. Starts out null, will be updated/owned by the golang code.
|
||||
service:
|
||||
name: #@ data.values.app_name + "-api"
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceNameWithSuffix("api")
|
||||
namespace: #@ namespace()
|
||||
port: 443
|
||||
|
30
deploy/concierge/helpers.lib.yaml
Normal file
30
deploy/concierge/helpers.lib.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
#! Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("@ytt:template", "template")
|
||||
|
||||
#@ def defaultResourceName():
|
||||
#@ return data.values.app_name
|
||||
#@ end
|
||||
|
||||
#@ def defaultResourceNameWithSuffix(suffix):
|
||||
#@ return data.values.app_name + "-" + suffix
|
||||
#@ end
|
||||
|
||||
#@ def namespace():
|
||||
#@ if data.values.into_namespace:
|
||||
#@ return data.values.into_namespace
|
||||
#@ else:
|
||||
#@ return data.values.namespace
|
||||
#@ end
|
||||
#@ end
|
||||
|
||||
#@ def defaultLabel():
|
||||
app: #@ data.values.app_name
|
||||
#@ end
|
||||
|
||||
#@ def labels():
|
||||
_: #@ template.replace(defaultLabel())
|
||||
_: #@ template.replace(data.values.custom_labels)
|
||||
#@ end
|
@ -2,35 +2,38 @@
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix")
|
||||
|
||||
#! Give permission to various cluster-scoped objects
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-aggregated-api-server"
|
||||
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
|
||||
labels: #@ labels()
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [namespaces]
|
||||
verbs: [get, list, watch]
|
||||
- apiGroups: [apiregistration.k8s.io]
|
||||
resources: [apiservices]
|
||||
verbs: [create, get, list, patch, update, watch]
|
||||
- apiGroups: [admissionregistration.k8s.io]
|
||||
resources: [validatingwebhookconfigurations, mutatingwebhookconfigurations]
|
||||
verbs: [get, list, watch]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ namespaces ]
|
||||
verbs: [ get, list, watch ]
|
||||
- apiGroups: [ apiregistration.k8s.io ]
|
||||
resources: [ apiservices ]
|
||||
verbs: [ create, get, list, patch, update, watch ]
|
||||
- apiGroups: [ admissionregistration.k8s.io ]
|
||||
resources: [ validatingwebhookconfigurations, mutatingwebhookconfigurations ]
|
||||
verbs: [ get, list, watch ]
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-aggregated-api-server"
|
||||
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: #@ data.values.app_name + "-aggregated-api-server"
|
||||
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
#! Give permission to various objects within the app's own namespace
|
||||
@ -38,39 +41,41 @@ roleRef:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-aggregated-api-server"
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [services]
|
||||
verbs: [create, get, list, patch, update, watch]
|
||||
- apiGroups: [""]
|
||||
resources: [secrets]
|
||||
verbs: [create, get, list, patch, update, watch, delete]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ services ]
|
||||
verbs: [ create, get, list, patch, update, watch ]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ secrets ]
|
||||
verbs: [ create, get, list, patch, update, watch, delete ]
|
||||
#! We need to be able to CRUD pods in our namespace so we can reconcile the kube-cert-agent pods.
|
||||
- apiGroups: [""]
|
||||
resources: [pods]
|
||||
verbs: [create, get, list, patch, update, watch, delete]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ pods ]
|
||||
verbs: [ create, get, list, patch, update, watch, delete ]
|
||||
#! We need to be able to exec into pods in our namespace so we can grab the API server's private key
|
||||
- apiGroups: [""]
|
||||
resources: [pods/exec]
|
||||
verbs: [create]
|
||||
- apiGroups: [config.pinniped.dev, idp.pinniped.dev]
|
||||
resources: ["*"]
|
||||
verbs: [create, get, list, update, watch]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ pods/exec ]
|
||||
verbs: [ create ]
|
||||
- apiGroups: [ config.pinniped.dev, idp.pinniped.dev ]
|
||||
resources: [ "*" ]
|
||||
verbs: [ create, get, list, update, watch ]
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-aggregated-api-server"
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: #@ data.values.app_name + "-aggregated-api-server"
|
||||
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
#! Give permission to read pods in the kube-system namespace so we can find the API server's private key
|
||||
@ -78,25 +83,27 @@ roleRef:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-kube-system-pod-read"
|
||||
name: #@ defaultResourceNameWithSuffix("kube-system-pod-read")
|
||||
namespace: kube-system
|
||||
labels: #@ labels()
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [pods]
|
||||
verbs: [get, list, watch]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ pods ]
|
||||
verbs: [ get, list, watch ]
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-kube-system-pod-read"
|
||||
name: #@ defaultResourceNameWithSuffix("kube-system-pod-read")
|
||||
namespace: kube-system
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: #@ data.values.app_name + "-kube-system-pod-read"
|
||||
name: #@ defaultResourceNameWithSuffix("kube-system-pod-read")
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
#! Allow both authenticated and unauthenticated TokenCredentialRequests (i.e. allow all requests)
|
||||
@ -104,16 +111,18 @@ roleRef:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-create-token-credential-requests"
|
||||
name: #@ defaultResourceNameWithSuffix("create-token-credential-requests")
|
||||
labels: #@ labels()
|
||||
rules:
|
||||
- apiGroups: [login.pinniped.dev]
|
||||
resources: [tokencredentialrequests]
|
||||
verbs: [create]
|
||||
- apiGroups: [ login.pinniped.dev ]
|
||||
resources: [ tokencredentialrequests ]
|
||||
verbs: [ create ]
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-create-token-credential-requests"
|
||||
name: #@ defaultResourceNameWithSuffix("create-token-credential-requests")
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: system:authenticated
|
||||
@ -123,7 +132,7 @@ subjects:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: #@ data.values.app_name + "-create-token-credential-requests"
|
||||
name: #@ defaultResourceNameWithSuffix("create-token-credential-requests")
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
#! Give permissions for subjectaccessreviews, tokenreview that is needed by aggregated api servers
|
||||
@ -131,12 +140,13 @@ roleRef:
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: system:auth-delegator
|
||||
@ -147,12 +157,13 @@ roleRef:
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-extension-apiserver-authentication-reader"
|
||||
name: #@ defaultResourceNameWithSuffix("extension-apiserver-authentication-reader")
|
||||
namespace: kube-system
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: extension-apiserver-authentication-reader
|
||||
@ -163,23 +174,25 @@ roleRef:
|
||||
kind: Role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-cluster-info-lister-watcher"
|
||||
name: #@ defaultResourceNameWithSuffix("cluster-info-lister-watcher")
|
||||
namespace: kube-public
|
||||
labels: #@ labels()
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [configmaps]
|
||||
verbs: [list, watch]
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ configmaps ]
|
||||
verbs: [ list, watch ]
|
||||
---
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-cluster-info-lister-watcher"
|
||||
name: #@ defaultResourceNameWithSuffix("cluster-info-lister-watcher")
|
||||
namespace: kube-public
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: #@ data.values.app_name + "-cluster-info-lister-watcher"
|
||||
name: #@ defaultResourceNameWithSuffix("cluster-info-lister-watcher")
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
@ -5,7 +5,21 @@
|
||||
---
|
||||
|
||||
app_name: pinniped-concierge
|
||||
|
||||
#! Creates a new namespace statically in yaml with the given name and installs the app into that namespace.
|
||||
namespace: pinniped-concierge
|
||||
#! If specified, assumes that a namespace of the given name already exists and installs the app into that namespace.
|
||||
#! If both `namespace` and `into_namespace` are specified, then only `into_namespace` is used.
|
||||
into_namespace: #! e.g. my-preexisting-namespace
|
||||
|
||||
#! All resources created statically by yaml at install-time and all resources created dynamically
|
||||
#! by controllers at runtime will be labelled with `app: $app_name` and also with the labels
|
||||
#! specified here. The value of `custom_labels` must be a map of string keys to string values.
|
||||
#! The app can be uninstalled either by:
|
||||
#! 1. Deleting the static install-time yaml resources including the static namespace, which will cascade and also delete
|
||||
#! resources that were dynamically created by controllers at runtime
|
||||
#! 2. Or, deleting all resources by label, which does not assume that there was a static install-time yaml namespace.
|
||||
custom_labels: {} #! e.g. {myCustomLabelName: myCustomLabelValue, otherCustomLabelName: otherCustomLabelValue}
|
||||
|
||||
#! Specify how many replicas of the Pinniped server to run.
|
||||
replicas: 2
|
||||
@ -20,7 +34,7 @@ image_tag: latest
|
||||
#! By default, the same image specified for image_repo/image_digest/image_tag will be re-used.
|
||||
kube_cert_agent_image:
|
||||
|
||||
#! Specifies a secret to be used when pulling the above container image.
|
||||
#! Specifies a secret to be used when pulling the above `image_repo` 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.
|
||||
|
17
deploy/concierge/z0_crd_overlay.yaml
Normal file
17
deploy/concierge/z0_crd_overlay.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
#! Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:overlay", "overlay")
|
||||
#@ load("helpers.lib.yaml", "labels")
|
||||
|
||||
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"credentialissuerconfigs.config.pinniped.dev"}}), expects=1
|
||||
---
|
||||
metadata:
|
||||
#@overlay/match missing_ok=True
|
||||
labels: #@ labels()
|
||||
|
||||
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"webhookidentityproviders.idp.pinniped.dev"}}), expects=1
|
||||
---
|
||||
metadata:
|
||||
#@overlay/match missing_ok=True
|
||||
labels: #@ labels()
|
@ -9,7 +9,7 @@ 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.
|
||||
#! Specifies a secret to be used when pulling the above `image_repo` 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.
|
||||
|
@ -2,42 +2,43 @@
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("@ytt:json", "json")
|
||||
#@ load("helpers.lib.yaml", "defaultLabel", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix")
|
||||
|
||||
#@ if not data.values.into_namespace:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: #@ data.values.namespace
|
||||
labels:
|
||||
name: #@ data.values.namespace
|
||||
labels: #@ labels()
|
||||
#@ end
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-static-config"
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceNameWithSuffix("static-config")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
data:
|
||||
#@yaml/text-templated-strings
|
||||
pinniped.yaml: |
|
||||
names:
|
||||
dynamicConfigMap: (@= data.values.app_name + "-dynamic-config" @)
|
||||
labels: (@= json.encode(labels()).rstrip() @)
|
||||
---
|
||||
#@ 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
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: #@ data.values.image_pull_dockerconfigjson
|
||||
@ -46,27 +47,24 @@ data:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
replicas: #@ data.values.replicas
|
||||
selector:
|
||||
matchLabels:
|
||||
app: #@ data.values.app_name
|
||||
matchLabels: #@ defaultLabel()
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
labels: #@ defaultLabel()
|
||||
spec:
|
||||
serviceAccountName: #@ data.values.app_name
|
||||
serviceAccountName: #@ defaultResourceName()
|
||||
#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "":
|
||||
imagePullSecrets:
|
||||
- name: image-pull-secret
|
||||
#@ end
|
||||
containers:
|
||||
- name: pinniped-supervisor
|
||||
- name: #@ defaultResourceName()
|
||||
#@ if data.values.image_digest:
|
||||
image: #@ data.values.image_repo + "@" + data.values.image_digest
|
||||
#@ else:
|
||||
@ -89,7 +87,7 @@ spec:
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: #@ data.values.app_name + "-static-config"
|
||||
name: #@ defaultResourceNameWithSuffix("static-config")
|
||||
- name: podinfo
|
||||
downwardAPI:
|
||||
items:
|
||||
@ -107,6 +105,5 @@ spec:
|
||||
- weight: 50
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: #@ data.values.app_name
|
||||
matchLabels: #@ defaultLabel()
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
30
deploy/supervisor/helpers.lib.yaml
Normal file
30
deploy/supervisor/helpers.lib.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
#! Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("@ytt:template", "template")
|
||||
|
||||
#@ def defaultResourceName():
|
||||
#@ return data.values.app_name
|
||||
#@ end
|
||||
|
||||
#@ def defaultResourceNameWithSuffix(suffix):
|
||||
#@ return data.values.app_name + "-" + suffix
|
||||
#@ end
|
||||
|
||||
#@ def namespace():
|
||||
#@ if data.values.into_namespace:
|
||||
#@ return data.values.into_namespace
|
||||
#@ else:
|
||||
#@ return data.values.namespace
|
||||
#@ end
|
||||
#@ end
|
||||
|
||||
#@ def defaultLabel():
|
||||
app: #@ data.values.app_name
|
||||
#@ end
|
||||
|
||||
#@ def labels():
|
||||
_: #@ template.replace(defaultLabel())
|
||||
_: #@ template.replace(data.values.custom_labels)
|
||||
#@ end
|
@ -2,16 +2,16 @@
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix")
|
||||
|
||||
#! 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
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [secrets]
|
||||
@ -23,15 +23,14 @@ rules:
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: #@ data.values.app_name
|
||||
namespace: #@ data.values.namespace
|
||||
name: #@ defaultResourceName()
|
||||
namespace: #@ namespace()
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: #@ data.values.app_name
|
||||
name: #@ defaultResourceName()
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
@ -1,14 +1,17 @@
|
||||
#! Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("helpers.lib.yaml", "defaultLabel", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix")
|
||||
|
||||
#@ 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
|
||||
name: #@ defaultResourceNameWithSuffix("nodeport")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
@ -25,14 +28,12 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-clusterip"
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceNameWithSuffix("clusterip")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: #@ data.values.app_name
|
||||
selector: #@ defaultLabel()
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: #@ data.values.service_clusterip_port
|
||||
@ -44,14 +45,12 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: #@ data.values.app_name + "-loadbalancer"
|
||||
namespace: #@ data.values.namespace
|
||||
labels:
|
||||
app: #@ data.values.app_name
|
||||
name: #@ defaultResourceNameWithSuffix("loadbalancer")
|
||||
namespace: #@ namespace()
|
||||
labels: #@ labels()
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
app: #@ data.values.app_name
|
||||
selector: #@ defaultLabel()
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: #@ data.values.service_loadbalancer_port
|
||||
|
@ -5,7 +5,21 @@
|
||||
---
|
||||
|
||||
app_name: pinniped-supervisor
|
||||
|
||||
#! Creates a new namespace statically in yaml with the given name and installs the app into that namespace.
|
||||
namespace: pinniped-supervisor
|
||||
#! If specified, assumes that a namespace of the given name already exists and installs the app into that namespace.
|
||||
#! If both `namespace` and `into_namespace` are specified, then only `into_namespace` is used.
|
||||
into_namespace: #! e.g. my-preexisting-namespace
|
||||
|
||||
#! All resources created statically by yaml at install-time and all resources created dynamically
|
||||
#! by controllers at runtime will be labelled with `app: $app_name` and also with the labels
|
||||
#! specified here. The value of `custom_labels` must be a map of string keys to string values.
|
||||
#! The app can be uninstalled either by:
|
||||
#! 1. Deleting the static install-time yaml resources including the static namespace, which will cascade and also delete
|
||||
#! resources that were dynamically created by controllers at runtime
|
||||
#! 2. Or, deleting all resources by label, which does not assume that there was a static install-time yaml namespace.
|
||||
custom_labels: {} #! e.g. {myCustomLabelName: myCustomLabelValue, otherCustomLabelName: otherCustomLabelValue}
|
||||
|
||||
#! Specify how many replicas of the Pinniped server to run.
|
||||
replicas: 2
|
||||
@ -15,7 +29,7 @@ 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.
|
||||
#! Specifies a secret to be used when pulling the above `image_repo` 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.
|
||||
|
11
deploy/supervisor/z0_crd_overlay.yaml
Normal file
11
deploy/supervisor/z0_crd_overlay.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
#! Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:overlay", "overlay")
|
||||
#@ load("helpers.lib.yaml", "labels")
|
||||
|
||||
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"oidcproviderconfigs.config.pinniped.dev"}}), expects=1
|
||||
---
|
||||
metadata:
|
||||
#@overlay/match missing_ok=True
|
||||
labels: #@ labels()
|
@ -94,6 +94,7 @@ k8s_yaml(local([
|
||||
'--data-value', 'image_tag=tilt-dev',
|
||||
'--data-value-yaml', 'replicas=1',
|
||||
'--data-value-yaml', 'service_nodeport_port=31234',
|
||||
'--data-value-yaml', 'custom_labels={mySupervisorCustomLabelName: mySupervisorCustomLabelValue}',
|
||||
]))
|
||||
# Tell tilt to watch all of those files for changes.
|
||||
watch_file('../../../deploy/supervisor')
|
||||
@ -135,7 +136,8 @@ k8s_yaml(local([
|
||||
'--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 ' +
|
||||
'--data-value-yaml "custom_labels={myConciergeCustomLabelName: myConciergeCustomLabelValue}"'
|
||||
]))
|
||||
# Tell tilt to watch all of those files for changes.
|
||||
watch_file('../../../deploy/concierge')
|
||||
|
@ -212,6 +212,7 @@ kubectl create secret generic "$test_username" \
|
||||
#
|
||||
supervisor_app_name="pinniped-supervisor"
|
||||
supervisor_namespace="supervisor"
|
||||
supervisor_custom_labels="{mySupervisorCustomLabelName: mySupervisorCustomLabelValue}"
|
||||
|
||||
if ! tilt_mode; then
|
||||
pushd deploy/supervisor >/dev/null
|
||||
@ -222,6 +223,7 @@ if ! tilt_mode; then
|
||||
--data-value "namespace=$supervisor_namespace" \
|
||||
--data-value "image_repo=$registry_repo" \
|
||||
--data-value "image_tag=$tag" \
|
||||
--data-value-yaml "custom_labels=$supervisor_custom_labels" \
|
||||
--data-value-yaml 'service_nodeport_port=31234' >"$manifest"
|
||||
|
||||
kapp deploy --yes --app "$supervisor_app_name" --diff-changes --file "$manifest"
|
||||
@ -230,21 +232,23 @@ if ! tilt_mode; then
|
||||
fi
|
||||
|
||||
#
|
||||
# Deploy Pinniped
|
||||
# Deploy the Pinniped Concierge
|
||||
#
|
||||
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}')"
|
||||
concierge_custom_labels="{myConciergeCustomLabelName: myConciergeCustomLabelValue}"
|
||||
|
||||
if ! tilt_mode; then
|
||||
pushd deploy/concierge >/dev/null
|
||||
|
||||
log_note "Deploying the Pinniped app to the cluster..."
|
||||
log_note "Deploying the Pinniped Concierge app to the cluster..."
|
||||
ytt --file . \
|
||||
--data-value "app_name=$concierge_app_name" \
|
||||
--data-value "namespace=$concierge_namespace" \
|
||||
--data-value-yaml "custom_labels=$concierge_custom_labels" \
|
||||
--data-value "image_repo=$registry_repo" \
|
||||
--data-value "image_tag=$tag" \
|
||||
--data-value "discovery_url=$discovery_url" >"$manifest"
|
||||
@ -264,6 +268,7 @@ cat <<EOF >/tmp/integration-test-env
|
||||
# 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_CONCIERGE_CUSTOM_LABELS='${concierge_custom_labels}'
|
||||
export PINNIPED_TEST_USER_USERNAME=${test_username}
|
||||
export PINNIPED_TEST_USER_GROUPS=${test_groups}
|
||||
export PINNIPED_TEST_USER_TOKEN=${test_username}:${test_password}
|
||||
@ -271,6 +276,7 @@ export PINNIPED_TEST_WEBHOOK_ENDPOINT=${webhook_url}
|
||||
export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle}
|
||||
export PINNIPED_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace}
|
||||
export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name}
|
||||
export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}'
|
||||
export PINNIPED_TEST_SUPERVISOR_ADDRESS="127.0.0.1:12345"
|
||||
export PINNIPED_TEST_CLI_OIDC_ISSUER=http://127.0.0.1:12346/dex
|
||||
export PINNIPED_TEST_CLI_OIDC_CLIENT_ID=pinniped-cli
|
||||
|
@ -17,13 +17,13 @@ import (
|
||||
loginv1alpha1 "go.pinniped.dev/generated/1.19/apis/login/v1alpha1"
|
||||
"go.pinniped.dev/internal/certauthority/dynamiccertauthority"
|
||||
"go.pinniped.dev/internal/concierge/apiserver"
|
||||
"go.pinniped.dev/internal/config/concierge"
|
||||
"go.pinniped.dev/internal/controller/identityprovider/idpcache"
|
||||
"go.pinniped.dev/internal/controllermanager"
|
||||
"go.pinniped.dev/internal/downward"
|
||||
"go.pinniped.dev/internal/dynamiccert"
|
||||
"go.pinniped.dev/internal/here"
|
||||
"go.pinniped.dev/internal/registry/credentialrequest"
|
||||
"go.pinniped.dev/pkg/config"
|
||||
)
|
||||
|
||||
// App is an object that represents the pinniped-concierge application.
|
||||
@ -92,7 +92,7 @@ func addCommandlineFlagsToCommand(cmd *cobra.Command, app *App) {
|
||||
// Boot the aggregated API server, which will in turn boot the controllers.
|
||||
func (a *App) runServer(ctx context.Context) error {
|
||||
// Read the server config file.
|
||||
cfg, err := config.FromPath(a.configPath)
|
||||
cfg, err := concierge.FromPath(a.configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not load config: %w", err)
|
||||
}
|
||||
@ -124,6 +124,7 @@ func (a *App) runServer(ctx context.Context) error {
|
||||
&controllermanager.Config{
|
||||
ServerInstallationNamespace: serverInstallationNamespace,
|
||||
NamesConfig: &cfg.NamesConfig,
|
||||
Labels: cfg.Labels,
|
||||
KubeCertAgentConfig: &cfg.KubeCertAgentConfig,
|
||||
DiscoveryURLOverride: cfg.DiscoveryInfo.URL,
|
||||
DynamicServingCertProvider: dynamicServingCertProvider,
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package config contains functionality to load/store api.Config's from/to
|
||||
// Package concierge contains functionality to load/store Config's from/to
|
||||
// some source.
|
||||
package config
|
||||
package concierge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/pkg/config/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -21,20 +20,20 @@ const (
|
||||
about9Months = 60 * 60 * 24 * 30 * 9
|
||||
)
|
||||
|
||||
// FromPath loads an api.Config from a provided local file path, inserts any
|
||||
// defaults (from the api.Config documentation), and verifies that the config is
|
||||
// valid (per the api.Config documentation).
|
||||
// FromPath loads an Config from a provided local file path, inserts any
|
||||
// defaults (from the Config documentation), and verifies that the config is
|
||||
// valid (per the Config documentation).
|
||||
//
|
||||
// Note! The api.Config file should contain base64-encoded WebhookCABundle data.
|
||||
// Note! The Config file should contain base64-encoded WebhookCABundle data.
|
||||
// This function will decode that base64-encoded data to PEM bytes to be stored
|
||||
// in the api.Config.
|
||||
func FromPath(path string) (*api.Config, error) {
|
||||
// in the Config.
|
||||
func FromPath(path string) (*Config, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read file: %w", err)
|
||||
}
|
||||
|
||||
var config api.Config
|
||||
var config Config
|
||||
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("decode yaml: %w", err)
|
||||
}
|
||||
@ -50,10 +49,14 @@ func FromPath(path string) (*api.Config, error) {
|
||||
return nil, fmt.Errorf("validate names: %w", err)
|
||||
}
|
||||
|
||||
if config.Labels == nil {
|
||||
config.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func maybeSetAPIDefaults(apiConfig *api.APIConfigSpec) {
|
||||
func maybeSetAPIDefaults(apiConfig *APIConfigSpec) {
|
||||
if apiConfig.ServingCertificateConfig.DurationSeconds == nil {
|
||||
apiConfig.ServingCertificateConfig.DurationSeconds = int64Ptr(aboutAYear)
|
||||
}
|
||||
@ -63,7 +66,7 @@ func maybeSetAPIDefaults(apiConfig *api.APIConfigSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
func maybeSetKubeCertAgentDefaults(cfg *api.KubeCertAgentSpec) {
|
||||
func maybeSetKubeCertAgentDefaults(cfg *KubeCertAgentSpec) {
|
||||
if cfg.NamePrefix == nil {
|
||||
cfg.NamePrefix = stringPtr("pinniped-kube-cert-agent-")
|
||||
}
|
||||
@ -73,7 +76,7 @@ func maybeSetKubeCertAgentDefaults(cfg *api.KubeCertAgentSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
func validateNames(names *api.NamesConfigSpec) error {
|
||||
func validateNames(names *NamesConfigSpec) error {
|
||||
missingNames := []string{}
|
||||
if names == nil {
|
||||
missingNames = append(missingNames, "servingCertificateSecret", "credentialIssuerConfig", "apiService")
|
||||
@ -94,7 +97,7 @@ func validateNames(names *api.NamesConfigSpec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAPI(apiConfig *api.APIConfigSpec) error {
|
||||
func validateAPI(apiConfig *APIConfigSpec) error {
|
||||
if *apiConfig.ServingCertificateConfig.DurationSeconds < *apiConfig.ServingCertificateConfig.RenewBeforeSeconds {
|
||||
return constable.Error("durationSeconds cannot be smaller than renewBeforeSeconds")
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
package concierge
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@ -11,14 +11,13 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.pinniped.dev/internal/here"
|
||||
"go.pinniped.dev/pkg/config/api"
|
||||
)
|
||||
|
||||
func TestFromPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
yaml string
|
||||
wantConfig *api.Config
|
||||
wantConfig *Config
|
||||
wantError string
|
||||
}{
|
||||
{
|
||||
@ -36,27 +35,34 @@ func TestFromPath(t *testing.T) {
|
||||
credentialIssuerConfig: pinniped-config
|
||||
apiService: pinniped-api
|
||||
kubeCertAgentPrefix: kube-cert-agent-prefix
|
||||
labels:
|
||||
myLabelKey1: myLabelValue1
|
||||
myLabelKey2: myLabelValue2
|
||||
KubeCertAgent:
|
||||
namePrefix: kube-cert-agent-name-prefix-
|
||||
image: kube-cert-agent-image
|
||||
imagePullSecrets: [kube-cert-agent-image-pull-secret]
|
||||
`),
|
||||
wantConfig: &api.Config{
|
||||
DiscoveryInfo: api.DiscoveryInfoSpec{
|
||||
wantConfig: &Config{
|
||||
DiscoveryInfo: DiscoveryInfoSpec{
|
||||
URL: stringPtr("https://some.discovery/url"),
|
||||
},
|
||||
APIConfig: api.APIConfigSpec{
|
||||
ServingCertificateConfig: api.ServingCertificateConfigSpec{
|
||||
APIConfig: APIConfigSpec{
|
||||
ServingCertificateConfig: ServingCertificateConfigSpec{
|
||||
DurationSeconds: int64Ptr(3600),
|
||||
RenewBeforeSeconds: int64Ptr(2400),
|
||||
},
|
||||
},
|
||||
NamesConfig: api.NamesConfigSpec{
|
||||
NamesConfig: NamesConfigSpec{
|
||||
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
|
||||
CredentialIssuerConfig: "pinniped-config",
|
||||
APIService: "pinniped-api",
|
||||
},
|
||||
KubeCertAgentConfig: api.KubeCertAgentSpec{
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
KubeCertAgentConfig: KubeCertAgentSpec{
|
||||
NamePrefix: stringPtr("kube-cert-agent-name-prefix-"),
|
||||
Image: stringPtr("kube-cert-agent-image"),
|
||||
ImagePullSecrets: []string{"kube-cert-agent-image-pull-secret"},
|
||||
@ -72,22 +78,23 @@ func TestFromPath(t *testing.T) {
|
||||
credentialIssuerConfig: pinniped-config
|
||||
apiService: pinniped-api
|
||||
`),
|
||||
wantConfig: &api.Config{
|
||||
DiscoveryInfo: api.DiscoveryInfoSpec{
|
||||
wantConfig: &Config{
|
||||
DiscoveryInfo: DiscoveryInfoSpec{
|
||||
URL: nil,
|
||||
},
|
||||
APIConfig: api.APIConfigSpec{
|
||||
ServingCertificateConfig: api.ServingCertificateConfigSpec{
|
||||
APIConfig: APIConfigSpec{
|
||||
ServingCertificateConfig: ServingCertificateConfigSpec{
|
||||
DurationSeconds: int64Ptr(60 * 60 * 24 * 365), // about a year
|
||||
RenewBeforeSeconds: int64Ptr(60 * 60 * 24 * 30 * 9), // about 9 months
|
||||
},
|
||||
},
|
||||
NamesConfig: api.NamesConfigSpec{
|
||||
NamesConfig: NamesConfigSpec{
|
||||
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
|
||||
CredentialIssuerConfig: "pinniped-config",
|
||||
APIService: "pinniped-api",
|
||||
},
|
||||
KubeCertAgentConfig: api.KubeCertAgentSpec{
|
||||
Labels: map[string]string{},
|
||||
KubeCertAgentConfig: KubeCertAgentSpec{
|
||||
NamePrefix: stringPtr("pinniped-kube-cert-agent-"),
|
||||
Image: stringPtr("debian:latest"),
|
||||
},
|
@ -1,19 +1,20 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package concierge
|
||||
|
||||
// Config contains knobs to setup an instance of Pinniped.
|
||||
// Config contains knobs to setup an instance of the Pinniped Concierge.
|
||||
type Config struct {
|
||||
DiscoveryInfo DiscoveryInfoSpec `json:"discovery"`
|
||||
APIConfig APIConfigSpec `json:"api"`
|
||||
NamesConfig NamesConfigSpec `json:"names"`
|
||||
KubeCertAgentConfig KubeCertAgentSpec `json:"kubeCertAgent"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// DiscoveryInfoSpec contains configuration knobs specific to
|
||||
// pinniped's publishing of discovery information. These values can be
|
||||
// viewed as overrides, i.e., if these are set, then pinniped will
|
||||
// viewed as overrides, i.e., if these are set, then Pinniped will
|
||||
// publish these values in its discovery document instead of the ones it finds.
|
||||
type DiscoveryInfoSpec struct {
|
||||
// URL contains the URL at which pinniped can be contacted.
|
||||
@ -26,7 +27,7 @@ type APIConfigSpec struct {
|
||||
ServingCertificateConfig ServingCertificateConfigSpec `json:"servingCertificate"`
|
||||
}
|
||||
|
||||
// NamesConfigSpec configures the names of some Kubernetes resources for Pinniped.
|
||||
// NamesConfigSpec configures the names of some Kubernetes resources for the Concierge.
|
||||
type NamesConfigSpec struct {
|
||||
ServingCertificateSecret string `json:"servingCertificateSecret"`
|
||||
CredentialIssuerConfig string `json:"credentialIssuerConfig"`
|
34
internal/config/supervisor/config.go
Normal file
34
internal/config/supervisor/config.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package supervisor contains functionality to load/store Config's from/to
|
||||
// some source.
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// FromPath loads an Config from a provided local file path, inserts any
|
||||
// defaults (from the Config documentation), and verifies that the config is
|
||||
// valid (Config documentation).
|
||||
func FromPath(path string) (*Config, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read file: %w", err)
|
||||
}
|
||||
|
||||
var config Config
|
||||
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("decode yaml: %w", err)
|
||||
}
|
||||
|
||||
if config.Labels == nil {
|
||||
config.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
69
internal/config/supervisor/config_test.go
Normal file
69
internal/config/supervisor/config_test.go
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.pinniped.dev/internal/here"
|
||||
)
|
||||
|
||||
func TestFromPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
yaml string
|
||||
wantConfig *Config
|
||||
}{
|
||||
{
|
||||
name: "Happy",
|
||||
yaml: here.Doc(`
|
||||
---
|
||||
labels:
|
||||
myLabelKey1: myLabelValue1
|
||||
myLabelKey2: myLabelValue2
|
||||
`),
|
||||
wantConfig: &Config{
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "When only the required fields are present, causes other fields to be defaulted",
|
||||
yaml: here.Doc(`
|
||||
---
|
||||
`),
|
||||
wantConfig: &Config{
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// Write yaml to temp file
|
||||
f, err := ioutil.TempFile("", "pinniped-test-config-yaml-*")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := os.Remove(f.Name())
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
_, err = f.WriteString(test.yaml)
|
||||
require.NoError(t, err)
|
||||
err = f.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test FromPath()
|
||||
config, err := FromPath(f.Name())
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.wantConfig, config)
|
||||
})
|
||||
}
|
||||
}
|
9
internal/config/supervisor/types.go
Normal file
9
internal/config/supervisor/types.go
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisor
|
||||
|
||||
// Config contains knobs to setup an instance of the Pinniped Supervisor.
|
||||
type Config struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
@ -29,6 +29,7 @@ const (
|
||||
type certsManagerController struct {
|
||||
namespace string
|
||||
certsSecretResourceName string
|
||||
certsSecretLabels map[string]string
|
||||
k8sClient kubernetes.Interface
|
||||
secretInformer corev1informers.SecretInformer
|
||||
|
||||
@ -43,6 +44,7 @@ type certsManagerController struct {
|
||||
func NewCertsManagerController(
|
||||
namespace string,
|
||||
certsSecretResourceName string,
|
||||
certsSecretLabels map[string]string,
|
||||
k8sClient kubernetes.Interface,
|
||||
secretInformer corev1informers.SecretInformer,
|
||||
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
||||
@ -57,6 +59,7 @@ func NewCertsManagerController(
|
||||
Syncer: &certsManagerController{
|
||||
namespace: namespace,
|
||||
certsSecretResourceName: certsSecretResourceName,
|
||||
certsSecretLabels: certsSecretLabels,
|
||||
k8sClient: k8sClient,
|
||||
secretInformer: secretInformer,
|
||||
certDuration: certDuration,
|
||||
@ -116,6 +119,7 @@ func (c *certsManagerController) Sync(ctx controllerlib.Context) error {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c.certsSecretResourceName,
|
||||
Namespace: c.namespace,
|
||||
Labels: c.certsSecretLabels,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
caCertificateSecretKey: string(aggregatedAPIServerCA.Bundle()),
|
||||
|
@ -42,6 +42,7 @@ func TestManagerControllerOptions(t *testing.T) {
|
||||
_ = NewCertsManagerController(
|
||||
installedInNamespace,
|
||||
certsSecretResourceName,
|
||||
make(map[string]string),
|
||||
nil,
|
||||
secretsInformer,
|
||||
observableWithInformerOption.WithInformer,
|
||||
@ -135,6 +136,10 @@ func TestManagerControllerSync(t *testing.T) {
|
||||
subject = NewCertsManagerController(
|
||||
installedInNamespace,
|
||||
certsSecretResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
kubeAPIClient,
|
||||
kubeInformers.Core().V1().Secrets(),
|
||||
controllerlib.WithInformer,
|
||||
@ -198,6 +203,10 @@ func TestManagerControllerSync(t *testing.T) {
|
||||
actualSecret := actualAction.GetObject().(*corev1.Secret)
|
||||
r.Equal(certsSecretResourceName, actualSecret.Name)
|
||||
r.Equal(installedInNamespace, actualSecret.Namespace)
|
||||
r.Equal(map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
}, actualSecret.Labels)
|
||||
actualCACert := actualSecret.StringData["caCertificate"]
|
||||
actualPrivateKey := actualSecret.StringData["tlsPrivateKey"]
|
||||
actualCertChain := actualSecret.StringData["tlsCertificateChain"]
|
||||
|
@ -21,6 +21,7 @@ func CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx context.Context,
|
||||
credentialIssuerConfigNamespace string,
|
||||
credentialIssuerConfigResourceName string,
|
||||
credentialIssuerConfigLabels map[string]string,
|
||||
pinnipedClient pinnipedclientset.Interface,
|
||||
applyUpdatesToCredentialIssuerConfigFunc func(configToUpdate *configv1alpha1.CredentialIssuerConfig),
|
||||
) error {
|
||||
@ -39,7 +40,9 @@ func CreateOrUpdateCredentialIssuerConfig(
|
||||
|
||||
if notFound {
|
||||
// Create it
|
||||
credentialIssuerConfig := minimalValidCredentialIssuerConfig(credentialIssuerConfigResourceName, credentialIssuerConfigNamespace)
|
||||
credentialIssuerConfig := minimalValidCredentialIssuerConfig(
|
||||
credentialIssuerConfigResourceName, credentialIssuerConfigNamespace, credentialIssuerConfigLabels,
|
||||
)
|
||||
applyUpdatesToCredentialIssuerConfigFunc(credentialIssuerConfig)
|
||||
|
||||
if _, err := credentialIssuerConfigsClient.Create(ctx, credentialIssuerConfig, metav1.CreateOptions{}); err != nil {
|
||||
@ -71,12 +74,14 @@ func CreateOrUpdateCredentialIssuerConfig(
|
||||
func minimalValidCredentialIssuerConfig(
|
||||
credentialIssuerConfigName string,
|
||||
credentialIssuerConfigNamespace string,
|
||||
credentialIssuerConfigLabels map[string]string,
|
||||
) *configv1alpha1.CredentialIssuerConfig {
|
||||
return &configv1alpha1.CredentialIssuerConfig{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerConfigName,
|
||||
Namespace: credentialIssuerConfigNamespace,
|
||||
Labels: credentialIssuerConfigLabels,
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerConfigStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerConfigStrategy{},
|
||||
|
@ -45,7 +45,15 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
|
||||
when("the config does not exist", func() {
|
||||
it("creates a new config which includes only the updates made by the func parameter", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
|
||||
configToUpdate.Status.KubeConfigInfo = &configv1alpha1.CredentialIssuerConfigKubeConfigInfo{
|
||||
CertificateAuthorityData: "some-ca-value",
|
||||
@ -64,6 +72,10 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerConfigResourceName,
|
||||
Namespace: installationNamespace,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerConfigStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerConfigStrategy{},
|
||||
@ -86,7 +98,12 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
it("returns an error", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {},
|
||||
)
|
||||
r.EqualError(err, "could not create or update credentialissuerconfig: create failed: error on create")
|
||||
@ -103,6 +120,9 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerConfigResourceName,
|
||||
Namespace: installationNamespace,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerConfigStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerConfigStrategy{
|
||||
@ -124,7 +144,15 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
it("updates the existing config to only apply the updates made by the func parameter", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
|
||||
configToUpdate.Status.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
},
|
||||
@ -142,7 +170,12 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
it("avoids the cost of an update if the local updates made by the func parameter did not actually change anything", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
|
||||
configToUpdate.Status.KubeConfigInfo.CertificateAuthorityData = "initial-ca-value"
|
||||
|
||||
@ -166,7 +199,12 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
it("returns an error", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {},
|
||||
)
|
||||
r.EqualError(err, "could not create or update credentialissuerconfig: get failed: error on get")
|
||||
@ -181,7 +219,12 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
it("returns an error", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
|
||||
configToUpdate.Status.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
},
|
||||
@ -215,7 +258,15 @@ func TestCreateOrUpdateCredentialIssuerConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
it("retries updates on conflict", func() {
|
||||
err := CreateOrUpdateCredentialIssuerConfig(ctx, installationNamespace, credentialIssuerConfigResourceName, pinnipedAPIClient,
|
||||
err := CreateOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
installationNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
|
||||
configToUpdate.Status.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ const (
|
||||
type kubeConigInfoPublisherController struct {
|
||||
credentialIssuerConfigNamespaceName string
|
||||
credentialIssuerConfigResourceName string
|
||||
credentialIssuerConfigLabels map[string]string
|
||||
serverOverride *string
|
||||
pinnipedClient pinnipedclientset.Interface
|
||||
configMapInformer corev1informers.ConfigMapInformer
|
||||
@ -38,6 +39,7 @@ type kubeConigInfoPublisherController struct {
|
||||
func NewKubeConfigInfoPublisherController(
|
||||
credentialIssuerConfigNamespaceName string,
|
||||
credentialIssuerConfigResourceName string,
|
||||
credentialIssuerConfigLabels map[string]string,
|
||||
serverOverride *string,
|
||||
pinnipedClient pinnipedclientset.Interface,
|
||||
configMapInformer corev1informers.ConfigMapInformer,
|
||||
@ -49,6 +51,7 @@ func NewKubeConfigInfoPublisherController(
|
||||
Syncer: &kubeConigInfoPublisherController{
|
||||
credentialIssuerConfigResourceName: credentialIssuerConfigResourceName,
|
||||
credentialIssuerConfigNamespaceName: credentialIssuerConfigNamespaceName,
|
||||
credentialIssuerConfigLabels: credentialIssuerConfigLabels,
|
||||
serverOverride: serverOverride,
|
||||
pinnipedClient: pinnipedClient,
|
||||
configMapInformer: configMapInformer,
|
||||
@ -114,6 +117,7 @@ func (c *kubeConigInfoPublisherController) Sync(ctx controllerlib.Context) error
|
||||
ctx.Context,
|
||||
c.credentialIssuerConfigNamespaceName,
|
||||
c.credentialIssuerConfigResourceName,
|
||||
c.credentialIssuerConfigLabels,
|
||||
c.pinnipedClient,
|
||||
updateServerAndCAFunc,
|
||||
)
|
||||
|
@ -43,6 +43,7 @@ func TestInformerFilters(t *testing.T) {
|
||||
_ = NewKubeConfigInfoPublisherController(
|
||||
installedInNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{},
|
||||
nil,
|
||||
nil,
|
||||
configMapInformer,
|
||||
@ -127,6 +128,10 @@ func TestSync(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerConfigResourceName,
|
||||
Namespace: expectedNamespace,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerConfigStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerConfigStrategy{},
|
||||
@ -146,6 +151,10 @@ func TestSync(t *testing.T) {
|
||||
subject = NewKubeConfigInfoPublisherController(
|
||||
installedInNamespace,
|
||||
credentialIssuerConfigResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
serverOverride,
|
||||
pinnipedAPIClient,
|
||||
kubeInformers.Core().V1().ConfigMaps(),
|
||||
|
@ -122,13 +122,7 @@ func (c *annotaterController) Sync(ctx controllerlib.Context) error {
|
||||
keyPath,
|
||||
); err != nil {
|
||||
err = fmt.Errorf("cannot update agent pod: %w", err)
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(
|
||||
ctx.Context,
|
||||
*c.credentialIssuerConfigLocationConfig,
|
||||
c.clock,
|
||||
c.pinnipedAPIClient,
|
||||
err,
|
||||
)
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(ctx.Context, *c.credentialIssuerConfigLocationConfig, nil, c.clock, c.pinnipedAPIClient, err)
|
||||
if strategyResultUpdateErr != nil {
|
||||
// If the CIC update fails, then we probably want to try again. This controller will get
|
||||
// called again because of the pod create failure, so just try the CIC update again then.
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
type createrController struct {
|
||||
agentPodConfig *AgentPodConfig
|
||||
credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig
|
||||
credentialIssuerConfigLabels map[string]string
|
||||
clock clock.Clock
|
||||
k8sClient kubernetes.Interface
|
||||
pinnipedAPIClient pinnipedclientset.Interface
|
||||
@ -38,6 +39,7 @@ type createrController struct {
|
||||
func NewCreaterController(
|
||||
agentPodConfig *AgentPodConfig,
|
||||
credentialIssuerConfigLocationConfig *CredentialIssuerConfigLocationConfig,
|
||||
credentialIssuerConfigLabels map[string]string,
|
||||
clock clock.Clock,
|
||||
k8sClient kubernetes.Interface,
|
||||
pinnipedAPIClient pinnipedclientset.Interface,
|
||||
@ -53,6 +55,7 @@ func NewCreaterController(
|
||||
Syncer: &createrController{
|
||||
agentPodConfig: agentPodConfig,
|
||||
credentialIssuerConfigLocationConfig: credentialIssuerConfigLocationConfig,
|
||||
credentialIssuerConfigLabels: credentialIssuerConfigLabels,
|
||||
clock: clock,
|
||||
k8sClient: k8sClient,
|
||||
pinnipedAPIClient: pinnipedAPIClient,
|
||||
@ -95,6 +98,7 @@ func (c *createrController) Sync(ctx controllerlib.Context) error {
|
||||
return createOrUpdateCredentialIssuerConfig(
|
||||
ctx.Context,
|
||||
*c.credentialIssuerConfigLocationConfig,
|
||||
c.credentialIssuerConfigLabels,
|
||||
c.clock,
|
||||
c.pinnipedAPIClient,
|
||||
constable.Error("did not find kube-controller-manager pod(s)"),
|
||||
@ -129,6 +133,7 @@ func (c *createrController) Sync(ctx controllerlib.Context) error {
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(
|
||||
ctx.Context,
|
||||
*c.credentialIssuerConfigLocationConfig,
|
||||
c.credentialIssuerConfigLabels,
|
||||
c.clock,
|
||||
c.pinnipedAPIClient,
|
||||
err,
|
||||
|
@ -42,7 +42,8 @@ func TestCreaterControllerFilter(t *testing.T) {
|
||||
_ = NewCreaterController(
|
||||
agentPodConfig,
|
||||
credentialIssuerConfigLocationConfig,
|
||||
nil, // clock, shound't matter
|
||||
map[string]string{},
|
||||
nil, // clock, shouldn't matter
|
||||
nil, // k8sClient, shouldn't matter
|
||||
nil, // pinnipedAPIClient, shouldn't matter
|
||||
kubeSystemPodInformer,
|
||||
@ -66,7 +67,8 @@ func TestCreaterControllerInitialEvent(t *testing.T) {
|
||||
_ = NewCreaterController(
|
||||
nil, // agentPodConfig, shouldn't matter
|
||||
nil, // credentialIssuerConfigLocationConfig, shouldn't matter
|
||||
nil, // clock, shound't matter
|
||||
map[string]string{},
|
||||
nil, // clock, shouldn't matter
|
||||
nil, // k8sClient, shouldn't matter
|
||||
nil, // pinnipedAPIClient, shouldn't matter
|
||||
kubeSystemInformers.Core().V1().Pods(),
|
||||
@ -111,11 +113,19 @@ func TestCreaterControllerSync(t *testing.T) {
|
||||
ContainerImage: "some-agent-image",
|
||||
PodNamePrefix: "some-agent-name-",
|
||||
ContainerImagePullSecrets: []string{"some-image-pull-secret"},
|
||||
AdditionalLabels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
&CredentialIssuerConfigLocationConfig{
|
||||
Namespace: credentialIssuerConfigNamespaceName,
|
||||
Name: credentialIssuerConfigResourceName,
|
||||
},
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
clock.NewFakeClock(frozenNow),
|
||||
kubeAPIClient,
|
||||
pinnipedAPIClient,
|
||||
@ -361,6 +371,10 @@ func TestCreaterControllerSync(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerConfigResourceName,
|
||||
Namespace: credentialIssuerConfigNamespaceName,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerConfigStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerConfigStrategy{
|
||||
@ -502,6 +516,10 @@ func TestCreaterControllerSync(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerConfigResourceName,
|
||||
Namespace: credentialIssuerConfigNamespaceName,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerConfigStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerConfigStrategy{
|
||||
|
@ -87,39 +87,21 @@ func (c *execerController) Sync(ctx controllerlib.Context) error {
|
||||
|
||||
certPEM, err := c.podCommandExecutor.Exec(agentPod.Namespace, agentPod.Name, "cat", certPath)
|
||||
if err != nil {
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(
|
||||
ctx.Context,
|
||||
*c.credentialIssuerConfigLocationConfig,
|
||||
c.clock,
|
||||
c.pinnipedAPIClient,
|
||||
err,
|
||||
)
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(ctx.Context, *c.credentialIssuerConfigLocationConfig, nil, c.clock, c.pinnipedAPIClient, err)
|
||||
klog.ErrorS(strategyResultUpdateErr, "could not create or update CredentialIssuerConfig with strategy success")
|
||||
return err
|
||||
}
|
||||
|
||||
keyPEM, err := c.podCommandExecutor.Exec(agentPod.Namespace, agentPod.Name, "cat", keyPath)
|
||||
if err != nil {
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(
|
||||
ctx.Context,
|
||||
*c.credentialIssuerConfigLocationConfig,
|
||||
c.clock,
|
||||
c.pinnipedAPIClient,
|
||||
err,
|
||||
)
|
||||
strategyResultUpdateErr := createOrUpdateCredentialIssuerConfig(ctx.Context, *c.credentialIssuerConfigLocationConfig, nil, c.clock, c.pinnipedAPIClient, err)
|
||||
klog.ErrorS(strategyResultUpdateErr, "could not create or update CredentialIssuerConfig with strategy success")
|
||||
return err
|
||||
}
|
||||
|
||||
c.dynamicCertProvider.Set([]byte(certPEM), []byte(keyPEM))
|
||||
|
||||
err = createOrUpdateCredentialIssuerConfig(
|
||||
ctx.Context,
|
||||
*c.credentialIssuerConfigLocationConfig,
|
||||
c.clock,
|
||||
c.pinnipedAPIClient,
|
||||
nil, // nil error = success! yay!
|
||||
)
|
||||
err = createOrUpdateCredentialIssuerConfig(ctx.Context, *c.credentialIssuerConfigLocationConfig, nil, c.clock, c.pinnipedAPIClient, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -61,12 +61,15 @@ type AgentPodConfig struct {
|
||||
// The container image used for the agent pods.
|
||||
ContainerImage string
|
||||
|
||||
// The name prefix for each of the agent pods.
|
||||
// The name prefix for each of the agent pods.
|
||||
PodNamePrefix string
|
||||
|
||||
// ContainerImagePullSecrets is a list of names of Kubernetes Secret objects that will be used as
|
||||
// ImagePullSecrets on the kube-cert-agent pods.
|
||||
ContainerImagePullSecrets []string
|
||||
|
||||
// Additional labels that should be added to every agent pod during creation.
|
||||
AdditionalLabels map[string]string
|
||||
}
|
||||
|
||||
type CredentialIssuerConfigLocationConfig struct {
|
||||
@ -78,9 +81,13 @@ type CredentialIssuerConfigLocationConfig struct {
|
||||
}
|
||||
|
||||
func (c *AgentPodConfig) Labels() map[string]string {
|
||||
return map[string]string{
|
||||
labels := map[string]string{
|
||||
agentPodLabelKey: agentPodLabelValue,
|
||||
}
|
||||
for k, v := range c.AdditionalLabels {
|
||||
labels[k] = v
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
func (c *AgentPodConfig) PodTemplate() *corev1.Pod {
|
||||
@ -258,9 +265,9 @@ func findControllerManagerPodForSpecificAgentPod(
|
||||
return maybeControllerManagerPod, nil
|
||||
}
|
||||
|
||||
func createOrUpdateCredentialIssuerConfig(
|
||||
ctx context.Context,
|
||||
func createOrUpdateCredentialIssuerConfig(ctx context.Context,
|
||||
cicConfig CredentialIssuerConfigLocationConfig,
|
||||
credentialIssuerConfigLabels map[string]string,
|
||||
clock clock.Clock,
|
||||
pinnipedAPIClient pinnipedclientset.Interface,
|
||||
err error,
|
||||
@ -269,6 +276,7 @@ func createOrUpdateCredentialIssuerConfig(
|
||||
ctx,
|
||||
cicConfig.Namespace,
|
||||
cicConfig.Name,
|
||||
credentialIssuerConfigLabels,
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerConfig) {
|
||||
var strategyResult configv1alpha1.CredentialIssuerConfigStrategy
|
||||
|
@ -79,6 +79,8 @@ func exampleControllerManagerAndAgentPods(
|
||||
Namespace: agentPodNamespace,
|
||||
Labels: map[string]string{
|
||||
"kube-cert-agent.pinniped.dev": "true",
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"kube-cert-agent.pinniped.dev/controller-manager-name": controllerManagerPod.Name,
|
||||
|
@ -56,15 +56,17 @@ func generateECKey(r io.Reader) (interface{}, error) {
|
||||
// jwkController holds the fields necessary for the JWKS controller to communicate with OPC's and
|
||||
// secrets, both via a cache and via the API.
|
||||
type jwksController struct {
|
||||
pinnipedClient pinnipedclientset.Interface
|
||||
kubeClient kubernetes.Interface
|
||||
opcInformer configinformers.OIDCProviderConfigInformer
|
||||
secretInformer corev1informers.SecretInformer
|
||||
jwksSecretLabels map[string]string
|
||||
pinnipedClient pinnipedclientset.Interface
|
||||
kubeClient kubernetes.Interface
|
||||
opcInformer configinformers.OIDCProviderConfigInformer
|
||||
secretInformer corev1informers.SecretInformer
|
||||
}
|
||||
|
||||
// NewJWKSController returns a controllerlib.Controller that ensures an OPC has a corresponding
|
||||
// Secret that contains a valid active JWK and JWKS.
|
||||
func NewJWKSController(
|
||||
jwksSecretLabels map[string]string,
|
||||
kubeClient kubernetes.Interface,
|
||||
pinnipedClient pinnipedclientset.Interface,
|
||||
secretInformer corev1informers.SecretInformer,
|
||||
@ -75,10 +77,11 @@ func NewJWKSController(
|
||||
controllerlib.Config{
|
||||
Name: "JWKSController",
|
||||
Syncer: &jwksController{
|
||||
kubeClient: kubeClient,
|
||||
pinnipedClient: pinnipedClient,
|
||||
secretInformer: secretInformer,
|
||||
opcInformer: opcInformer,
|
||||
jwksSecretLabels: jwksSecretLabels,
|
||||
kubeClient: kubeClient,
|
||||
pinnipedClient: pinnipedClient,
|
||||
secretInformer: secretInformer,
|
||||
opcInformer: opcInformer,
|
||||
},
|
||||
},
|
||||
// We want to be notified when a OPC's secret gets updated or deleted. When this happens, we
|
||||
@ -234,6 +237,7 @@ func (c *jwksController) generateSecret(opc *configv1alpha1.OIDCProviderConfig)
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: opc.Name + "-jwks",
|
||||
Namespace: opc.Namespace,
|
||||
Labels: c.jwksSecretLabels,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(opc, schema.GroupVersionKind{
|
||||
Group: configv1alpha1.SchemeGroupVersion.Group,
|
||||
@ -241,7 +245,6 @@ func (c *jwksController) generateSecret(opc *configv1alpha1.OIDCProviderConfig)
|
||||
Kind: opcKind,
|
||||
}),
|
||||
},
|
||||
// TODO: custom labels.
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
activeJWKKey: jwkData,
|
||||
|
@ -151,6 +151,7 @@ func TestJWKSControllerFilterSecret(t *testing.T) {
|
||||
).Config().V1alpha1().OIDCProviderConfigs()
|
||||
withInformer := testutil.NewObservableWithInformerOption()
|
||||
_ = NewJWKSController(
|
||||
nil, // labels, not needed
|
||||
nil, // kubeClient, not needed
|
||||
nil, // pinnipedClient, not needed
|
||||
secretInformer,
|
||||
@ -204,6 +205,7 @@ func TestJWKSControllerFilterOPC(t *testing.T) {
|
||||
).Config().V1alpha1().OIDCProviderConfigs()
|
||||
withInformer := testutil.NewObservableWithInformerOption()
|
||||
_ = NewJWKSController(
|
||||
nil, // labels, not needed
|
||||
nil, // kubeClient, not needed
|
||||
nil, // pinnipedClient, not needed
|
||||
secretInformer,
|
||||
@ -264,6 +266,10 @@ func TestJWKSControllerSync(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: goodOPCWithStatus.Status.JWKSSecret.Name,
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: opcGVR.GroupVersion().String(),
|
||||
@ -648,6 +654,10 @@ func TestJWKSControllerSync(t *testing.T) {
|
||||
)
|
||||
|
||||
c := NewJWKSController(
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
kubeAPIClient,
|
||||
pinnipedAPIClient,
|
||||
kubeInformers.Core().V1().Secrets(),
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
loginv1alpha1 "go.pinniped.dev/generated/1.19/apis/login/v1alpha1"
|
||||
pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned"
|
||||
pinnipedinformers "go.pinniped.dev/generated/1.19/client/informers/externalversions"
|
||||
"go.pinniped.dev/internal/config/concierge"
|
||||
"go.pinniped.dev/internal/controller/apicerts"
|
||||
"go.pinniped.dev/internal/controller/identityprovider/idpcache"
|
||||
"go.pinniped.dev/internal/controller/identityprovider/webhookcachecleaner"
|
||||
@ -30,7 +31,6 @@ import (
|
||||
"go.pinniped.dev/internal/controller/kubecertagent"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/dynamiccert"
|
||||
"go.pinniped.dev/pkg/config/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -47,11 +47,11 @@ type Config struct {
|
||||
|
||||
// NamesConfig comes from the Pinniped config API (see api.Config). It specifies how Kubernetes
|
||||
// objects should be named.
|
||||
NamesConfig *api.NamesConfigSpec
|
||||
NamesConfig *concierge.NamesConfigSpec
|
||||
|
||||
// KubeCertAgentConfig comes from the Pinniped config API (see api.Config). It configures how
|
||||
// the kubecertagent package's controllers should manage the agent pods.
|
||||
KubeCertAgentConfig *api.KubeCertAgentSpec
|
||||
KubeCertAgentConfig *concierge.KubeCertAgentSpec
|
||||
|
||||
// DiscoveryURLOverride allows a caller to inject a hardcoded discovery URL into Pinniped
|
||||
// discovery document.
|
||||
@ -72,6 +72,9 @@ type Config struct {
|
||||
|
||||
// IDPCache is a cache of authenticators shared amongst various IDP-related controllers.
|
||||
IDPCache *idpcache.Cache
|
||||
|
||||
// Labels are labels that should be added to any resources created by the controllers.
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Prepare the controllers and their informers and return a function that will start them when called.
|
||||
@ -96,6 +99,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
|
||||
ContainerImage: *c.KubeCertAgentConfig.Image,
|
||||
PodNamePrefix: *c.KubeCertAgentConfig.NamePrefix,
|
||||
ContainerImagePullSecrets: c.KubeCertAgentConfig.ImagePullSecrets,
|
||||
AdditionalLabels: c.Labels,
|
||||
}
|
||||
credentialIssuerConfigLocationConfig := &kubecertagent.CredentialIssuerConfigLocationConfig{
|
||||
Namespace: c.ServerInstallationNamespace,
|
||||
@ -112,6 +116,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
|
||||
issuerconfig.NewKubeConfigInfoPublisherController(
|
||||
c.ServerInstallationNamespace,
|
||||
c.NamesConfig.CredentialIssuerConfig,
|
||||
c.Labels,
|
||||
c.DiscoveryURLOverride,
|
||||
pinnipedClient,
|
||||
informers.kubePublicNamespaceK8s.Core().V1().ConfigMaps(),
|
||||
@ -125,6 +130,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
|
||||
apicerts.NewCertsManagerController(
|
||||
c.ServerInstallationNamespace,
|
||||
c.NamesConfig.ServingCertificateSecret,
|
||||
c.Labels,
|
||||
k8sClient,
|
||||
informers.installationNamespaceK8s.Core().V1().Secrets(),
|
||||
controllerlib.WithInformer,
|
||||
@ -174,6 +180,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
|
||||
kubecertagent.NewCreaterController(
|
||||
agentPodConfig,
|
||||
credentialIssuerConfigLocationConfig,
|
||||
c.Labels,
|
||||
clock.RealClock{},
|
||||
k8sClient,
|
||||
pinnipedClient,
|
||||
|
@ -90,6 +90,10 @@ func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) {
|
||||
require.NotEmpty(t, initialCACert)
|
||||
require.NotEmpty(t, initialPrivateKey)
|
||||
require.NotEmpty(t, initialCertChain)
|
||||
for k, v := range env.ConciergeCustomLabels {
|
||||
require.Equalf(t, v, secret.Labels[k], "expected secret to have label %s: %s", k, v)
|
||||
}
|
||||
require.Equal(t, env.ConciergeAppName, secret.Labels["app"])
|
||||
|
||||
// Check that the APIService has the same CA.
|
||||
apiService, err := aggregatedClient.ApiregistrationV1().APIServices().Get(ctx, apiServiceName, metav1.GetOptions{})
|
||||
@ -115,6 +119,10 @@ func TestAPIServingCertificateAutoCreationAndRotation(t *testing.T) {
|
||||
require.NotEqual(t, initialCACert, regeneratedCACert)
|
||||
require.NotEqual(t, initialPrivateKey, regeneratedPrivateKey)
|
||||
require.NotEqual(t, initialCertChain, regeneratedCertChain)
|
||||
for k, v := range env.ConciergeCustomLabels {
|
||||
require.Equalf(t, v, secret.Labels[k], "expected secret to have label `%s: %s`", k, v)
|
||||
}
|
||||
require.Equal(t, env.ConciergeAppName, secret.Labels["app"])
|
||||
|
||||
// Expect that the APIService was also updated with the new CA.
|
||||
aggregatedAPIUpdated := func() bool {
|
||||
|
@ -33,8 +33,14 @@ func TestCredentialIssuerConfig(t *testing.T) {
|
||||
|
||||
require.Len(t, actualConfigList.Items, 1)
|
||||
|
||||
actualConfig := actualConfigList.Items[0]
|
||||
actualStatusKubeConfigInfo := actualConfigList.Items[0].Status.KubeConfigInfo
|
||||
|
||||
for k, v := range env.ConciergeCustomLabels {
|
||||
require.Equalf(t, v, actualConfig.Labels[k], "expected cic to have label `%s: %s`", k, v)
|
||||
}
|
||||
require.Equal(t, env.ConciergeAppName, actualConfig.Labels["app"])
|
||||
|
||||
// Verify the cluster strategy status based on what's expected of the test cluster's ability to share signing keys.
|
||||
actualStatusStrategies := actualConfigList.Items[0].Status.Strategies
|
||||
require.Len(t, actualStatusStrategies, 1)
|
||||
|
@ -44,6 +44,14 @@ func TestKubeCertAgent(t *testing.T) {
|
||||
require.NotEmpty(t, originalAgentPods.Items)
|
||||
sortPods(originalAgentPods)
|
||||
|
||||
for _, agentPod := range originalAgentPods.Items {
|
||||
// All agent pods should contain all custom labels
|
||||
for k, v := range env.ConciergeCustomLabels {
|
||||
require.Equalf(t, v, agentPod.Labels[k], "expected agent pod to have label `%s: %s`", k, v)
|
||||
}
|
||||
require.Equal(t, env.ConciergeAppName, agentPod.Labels["app"])
|
||||
}
|
||||
|
||||
agentPodsReconciled := func() bool {
|
||||
var currentAgentPods *corev1.PodList
|
||||
currentAgentPods, err = kubeClient.CoreV1().Pods(env.ConciergeNamespace).List(ctx, metav1.ListOptions{
|
||||
|
@ -49,6 +49,12 @@ func TestSupervisorOIDCKeys(t *testing.T) {
|
||||
Get(ctx, updatedOPC.Status.JWKSSecret.Name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure that the secret was labelled.
|
||||
for k, v := range env.SupervisorCustomLabels {
|
||||
require.Equalf(t, v, secret.Labels[k], "expected secret to have label `%s: %s`", k, v)
|
||||
}
|
||||
require.Equal(t, env.SupervisorAppName, secret.Labels["app"])
|
||||
|
||||
// Ensure the secret has an active key.
|
||||
jwkData, ok := secret.Data["activeJWK"]
|
||||
require.True(t, ok, "secret is missing active jwk")
|
||||
|
@ -26,13 +26,15 @@ const (
|
||||
type TestEnv struct {
|
||||
t *testing.T
|
||||
|
||||
ConciergeNamespace string `json:"conciergeNamespace"`
|
||||
SupervisorNamespace string `json:"supervisorNamespace"`
|
||||
ConciergeAppName string `json:"conciergeAppName"`
|
||||
SupervisorAppName string `json:"supervisorAppName"`
|
||||
Capabilities map[Capability]bool `json:"capabilities"`
|
||||
TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"`
|
||||
SupervisorAddress string `json:"supervisorAddress"`
|
||||
ConciergeNamespace string `json:"conciergeNamespace"`
|
||||
SupervisorNamespace string `json:"supervisorNamespace"`
|
||||
ConciergeAppName string `json:"conciergeAppName"`
|
||||
SupervisorAppName string `json:"supervisorAppName"`
|
||||
SupervisorCustomLabels map[string]string `json:"supervisorCustomLabels"`
|
||||
ConciergeCustomLabels map[string]string `json:"conciergeCustomLabels"`
|
||||
Capabilities map[Capability]bool `json:"capabilities"`
|
||||
TestWebhook idpv1alpha1.WebhookIdentityProviderSpec `json:"testWebhook"`
|
||||
SupervisorAddress string `json:"supervisorAddress"`
|
||||
|
||||
TestUser struct {
|
||||
Token string `json:"token"`
|
||||
@ -89,6 +91,19 @@ func IntegrationEnv(t *testing.T) *TestEnv {
|
||||
result.SupervisorAddress = needEnv("PINNIPED_TEST_SUPERVISOR_ADDRESS")
|
||||
result.TestWebhook.TLS = &idpv1alpha1.TLSSpec{CertificateAuthorityData: needEnv("PINNIPED_TEST_WEBHOOK_CA_BUNDLE")}
|
||||
|
||||
conciergeCustomLabelsYAML := needEnv("PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS")
|
||||
var conciergeCustomLabels map[string]string
|
||||
err = yaml.Unmarshal([]byte(conciergeCustomLabelsYAML), &conciergeCustomLabels)
|
||||
require.NoErrorf(t, err, "PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS must be a YAML map of string to string")
|
||||
result.ConciergeCustomLabels = conciergeCustomLabels
|
||||
require.NotEmpty(t, result.ConciergeCustomLabels, "PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS cannot be empty")
|
||||
supervisorCustomLabelsYAML := needEnv("PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS")
|
||||
var supervisorCustomLabels map[string]string
|
||||
err = yaml.Unmarshal([]byte(supervisorCustomLabelsYAML), &supervisorCustomLabels)
|
||||
require.NoErrorf(t, err, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS must be a YAML map of string to string")
|
||||
result.SupervisorCustomLabels = supervisorCustomLabels
|
||||
require.NotEmpty(t, result.SupervisorCustomLabels, "PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS cannot be empty")
|
||||
|
||||
result.OIDCUpstream.Issuer = needEnv("PINNIPED_TEST_CLI_OIDC_ISSUER")
|
||||
result.OIDCUpstream.ClientID = needEnv("PINNIPED_TEST_CLI_OIDC_CLIENT_ID")
|
||||
result.OIDCUpstream.LocalhostPort, _ = strconv.Atoi(needEnv("PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT"))
|
||||
|
Loading…
Reference in New Issue
Block a user