diff --git a/go.mod b/go.mod index 39f761c8d..1aa49de43 100644 --- a/go.mod +++ b/go.mod @@ -11,9 +11,9 @@ require ( github.com/gocarina/gocsv v0.0.0-20190927101021-3ecffd272576 github.com/google/go-cmp v0.6.0 github.com/haproxytech/config-parser/v4 v4.0.0-rc1 - github.com/openshift/api v0.0.0-20240202140003-8b34b9854c7f - github.com/openshift/client-go v0.0.0-20230120202327-72f107311084 - github.com/openshift/library-go v0.0.0-20230120202744-256994f916c4 + github.com/openshift/api v0.0.0-20240424142232-29a704bf5aa2 + github.com/openshift/client-go v0.0.0-20240405120947-c67c8325cdd8 + github.com/openshift/library-go v0.0.0-20240426144148-0690e4a4602d github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_model v0.4.0 github.com/prometheus/common v0.44.0 diff --git a/go.sum b/go.sum index 9fa5db23a..056fed875 100644 --- a/go.sum +++ b/go.sum @@ -176,12 +176,12 @@ github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4 github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/openshift/api v0.0.0-20240202140003-8b34b9854c7f h1:yJ8mEpEW2aJo+97wguYL+ehCsJe/pbeKjFsbdMuuEJI= -github.com/openshift/api v0.0.0-20240202140003-8b34b9854c7f/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4= -github.com/openshift/client-go v0.0.0-20230120202327-72f107311084 h1:66uaqNwA+qYyQDwsMWUfjjau8ezmg1dzCqub13KZOcE= -github.com/openshift/client-go v0.0.0-20230120202327-72f107311084/go.mod h1:M3h9m001PWac3eAudGG3isUud6yBjr5XpzLYLLTlHKo= -github.com/openshift/library-go v0.0.0-20230120202744-256994f916c4 h1:cFYg18OROQMHlrGWL9HpV1elDKbnRFLz/ED5VvP3qvw= -github.com/openshift/library-go v0.0.0-20230120202744-256994f916c4/go.mod h1:wrrjvk/CK+nte9Wxend9/K6apy6Zep28lzM27/aJar8= +github.com/openshift/api v0.0.0-20240424142232-29a704bf5aa2 h1:U1BsjJoTsvYjymeMseC8apZnvCgExIIRolpc/xJ7jhM= +github.com/openshift/api v0.0.0-20240424142232-29a704bf5aa2/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4= +github.com/openshift/client-go v0.0.0-20240405120947-c67c8325cdd8 h1:HGfbllzRcrJBSiwzNjBCs7sExLUxC5/1evnvlNGB0Cg= +github.com/openshift/client-go v0.0.0-20240405120947-c67c8325cdd8/go.mod h1:+VvvaMSTUhOt+rBq7NwRLSNxq06hTeRCBqm0j0PQEq8= +github.com/openshift/library-go v0.0.0-20240426144148-0690e4a4602d h1:PVCZvkSfUEwiMEYQ5AL+FIN4HpUUkpduUSkC/1U43H4= +github.com/openshift/library-go v0.0.0-20240426144148-0690e4a4602d/go.mod h1:lFwyRj0XjUf25Da3Q00y+KuaxCWTJ6YzYPDX1+96nco= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/pkg/cmd/infra/router/router.go b/pkg/cmd/infra/router/router.go index c758cd217..4bc9503fc 100644 --- a/pkg/cmd/infra/router/router.go +++ b/pkg/cmd/infra/router/router.go @@ -78,6 +78,10 @@ type RouterSelection struct { // WatchEndpoints when true will watch Endpoints instead of // EndpointSlices. WatchEndpoints bool + + // AllowExternalCertificates when true enables RouteSecretManager plugin and the external certificate validation. + // The cluster-ingress-operator sets it if RouteExternalCertificate feature-gate is enabled. + AllowExternalCertificates bool } // Bind sets the appropriate labels @@ -107,6 +111,7 @@ func (o *RouterSelection) Bind(flag *pflag.FlagSet) { flag.MarkDeprecated("enable-ingress", "Ingress resources are now synchronized to routes automatically.") flag.StringVar(&o.ListenAddr, "listen-addr", env("ROUTER_LISTEN_ADDR", ""), "The name of an interface to listen on to expose metrics and health checking. If not specified, will not listen. Overrides stats port.") flag.BoolVar(&o.WatchEndpoints, "watch-endpoints", isTrue(env("ROUTER_WATCH_ENDPOINTS", "")), "Watch Endpoints instead of the EndpointSlice resource.") + flag.BoolVar(&o.AllowExternalCertificates, "allow-external-certificates", isTrue(env("ROUTER_ENABLE_EXTERNAL_CERTIFICATE", "")), "Enable RouteSecretManager plugin and validation of external certificates.") } // RouteUpdate updates the route before it is seen by the cache. diff --git a/pkg/cmd/infra/router/template.go b/pkg/cmd/infra/router/template.go index d92ae7060..14e5b2056 100644 --- a/pkg/cmd/infra/router/template.go +++ b/pkg/cmd/infra/router/template.go @@ -36,6 +36,7 @@ import ( routelisters "github.com/openshift/client-go/route/listers/route/v1" "github.com/openshift/library-go/pkg/crypto" "github.com/openshift/library-go/pkg/proc" + "github.com/openshift/library-go/pkg/route/secretmanager" "github.com/openshift/router/pkg/router" "github.com/openshift/router/pkg/router/controller" @@ -718,6 +719,10 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error { if err != nil { return err } + authorizationClient, err := authorizationclient.NewForConfig(config) + if err != nil { + return err + } var cfgManager templateplugin.ConfigManager var blueprintPlugin router.Plugin @@ -746,6 +751,8 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error { return err } + secretManager := secretmanager.NewManager(kc, nil) + pluginCfg := templateplugin.TemplatePluginConfig{ WorkingDir: o.WorkingDir, TemplatePath: o.TemplateFile, @@ -773,6 +780,9 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error { HTTPResponseHeaders: o.HTTPResponseHeaders, HTTPRequestHeaders: o.HTTPRequestHeaders, } + if o.AllowExternalCertificates { + pluginCfg.SecretManager = secretManager + } svcFetcher := templateplugin.NewListWatchServiceLookup(kc.CoreV1(), o.ResyncInterval, o.Namespace) templatePlugin, err := templateplugin.NewTemplatePlugin(pluginCfg, svcFetcher) @@ -804,6 +814,9 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error { if o.ExtendedValidation { plugin = controller.NewExtendedValidator(plugin, recorder) } + if o.AllowExternalCertificates { + plugin = controller.NewRouteSecretManager(plugin, recorder, secretManager, kc.CoreV1(), authorizationClient.SubjectAccessReviews()) + } plugin = controller.NewUniqueHost(plugin, o.RouterSelection.DisableNamespaceOwnershipCheck, recorder) plugin = controller.NewHostAdmitter(plugin, o.RouteAdmissionFunc(), o.AllowWildcardRoutes, o.RouterSelection.DisableNamespaceOwnershipCheck, recorder) diff --git a/pkg/router/controller/route_secret_manager.go b/pkg/router/controller/route_secret_manager.go new file mode 100644 index 000000000..bbb7ff262 --- /dev/null +++ b/pkg/router/controller/route_secret_manager.go @@ -0,0 +1,223 @@ +package controller + +import ( + "context" + "fmt" + + routev1 "github.com/openshift/api/route/v1" + "github.com/openshift/library-go/pkg/route/secretmanager" + "github.com/openshift/router/pkg/router" + "github.com/openshift/router/pkg/router/routeapihelpers" + kapi "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apimachinery/pkg/watch" + authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" +) + +// RouteSecretManager implements the router.Plugin interface to register +// or unregister route with secretManger if externalCertificate is used. +// It also reads the referenced secret to update in-memory tls.Certificate and tls.Key +type RouteSecretManager struct { + // plugin is the next plugin in the chain. + plugin router.Plugin + // recorder is an interface for indicating route status. + recorder RouteStatusRecorder + + secretManager secretmanager.SecretManager + secretsGetter corev1client.SecretsGetter + sarClient authorizationclient.SubjectAccessReviewInterface +} + +// NewRouteSecretManager creates a new instance of RouteSecretManager. +// It wraps the provided plugin and adds secret management capabilities. +func NewRouteSecretManager(plugin router.Plugin, recorder RouteStatusRecorder, secretManager secretmanager.SecretManager, secretsGetter corev1client.SecretsGetter, sarClient authorizationclient.SubjectAccessReviewInterface) *RouteSecretManager { + return &RouteSecretManager{ + plugin: plugin, + recorder: recorder, + secretManager: secretManager, + secretsGetter: secretsGetter, + sarClient: sarClient, + } +} + +func (p *RouteSecretManager) HandleNode(eventType watch.EventType, node *kapi.Node) error { + return p.plugin.HandleNode(eventType, node) +} + +func (p *RouteSecretManager) HandleEndpoints(eventType watch.EventType, endpoints *kapi.Endpoints) error { + return p.plugin.HandleEndpoints(eventType, endpoints) +} + +func (p *RouteSecretManager) HandleNamespaces(namespaces sets.String) error { + return p.plugin.HandleNamespaces(namespaces) +} + +func (p *RouteSecretManager) Commit() error { + return p.plugin.Commit() +} + +// HandleRoute manages the registration, unregistration, and validation of routes with external certificates. +// For Added events, it validates the route's external certificate configuration and registers it with the secret manager. +// For Modified events, it first unregisters the route if it's already registered and then revalidates and registers it again. +// For Deleted events, it unregisters the route if it's registered. +// Additionally, it delegates the handling of the event to the next plugin in the chain after performing the necessary actions. +func (p *RouteSecretManager) HandleRoute(eventType watch.EventType, route *routev1.Route) error { + log.V(10).Info("HandleRoute: RouteSecretManager", "eventType", eventType) + + switch eventType { + case watch.Added: + // register with secret monitor + if hasExternalCertificate(route) { + if err := p.validateAndRegister(route); err != nil { + return err + } + } + + // For Modified events always unregister and reregister the route even if the TLS configuration did not change. + // Since the `HandleRoute()` method does not carry the old route spec, + // and there's no definite way to compare old and new TLS configurations, + // assume that the TLS configuration is always updated, necessitating re-registration. + // Additionally, always creating a new `secretHandler` ensures that there are no stale route specs + // in the next plugin chain, especially when the referenced secret is updated or deleted. + // This prevents sending outdated routes to subsequent plugins, preserving expected functionality. + // TODO: Refer https://github.com/openshift/router/pull/565#discussion_r1596441128 for possible ways to improve the logic. + case watch.Modified: + // unregister associated secret monitor, if registered + if p.secretManager.IsRouteRegistered(route.Namespace, route.Name) { + if err := p.secretManager.UnregisterRoute(route.Namespace, route.Name); err != nil { + log.Error(err, "failed to unregister route") + return err + } + } + // register with secret monitor + if hasExternalCertificate(route) { + if err := p.validateAndRegister(route); err != nil { + return err + } + } + + case watch.Deleted: + // unregister associated secret monitor, if registered + if p.secretManager.IsRouteRegistered(route.Namespace, route.Name) { + if err := p.secretManager.UnregisterRoute(route.Namespace, route.Name); err != nil { + log.Error(err, "failed to unregister route") + return err + } + } + default: + return fmt.Errorf("invalid eventType %v", eventType) + } + + // call next plugin + return p.plugin.HandleRoute(eventType, route) +} + +// validateAndRegister validates the route's externalCertificate configuration and registers it with the secret manager. +// It also updates the in-memory TLS certificate and key after reading from secret informer's cache. +func (p *RouteSecretManager) validateAndRegister(route *routev1.Route) error { + fldPath := field.NewPath("spec").Child("tls").Child("externalCertificate") + // validate + if err := routeapihelpers.ValidateTLSExternalCertificate(route, fldPath, p.sarClient, p.secretsGetter).ToAggregate(); err != nil { + log.Error(err, "skipping route due to invalid externalCertificate configuration", "namespace", route.Namespace, "route", route.Name) + p.recorder.RecordRouteRejection(route, "ExternalCertificateValidationFailed", err.Error()) + p.plugin.HandleRoute(watch.Deleted, route) + return err + } + + // register route with secretManager + handler := p.generateSecretHandler(route) + if err := p.secretManager.RegisterRoute(context.TODO(), route.Namespace, route.Name, route.Spec.TLS.ExternalCertificate.Name, handler); err != nil { + log.Error(err, "failed to register route") + return err + } + // read referenced secret + secret, err := p.secretManager.GetSecret(context.TODO(), route.Namespace, route.Name) + if err != nil { + log.Error(err, "failed to get referenced secret") + return err + } + + // Update the tls.Certificate and tls.Key fields of the route with the data from the referenced secret. + // Since externalCertificate does not contain the CACertificate, tls.CACertificate will not be updated. + // NOTE that this update is only performed in-memory and will not reflect in the actual route resource stored in etcd, because + // the router does not make kube-client calls to directly update route resources. + route.Spec.TLS.Certificate = string(secret.Data["tls.crt"]) + route.Spec.TLS.Key = string(secret.Data["tls.key"]) + + return nil +} + +// generateSecretHandler creates ResourceEventHandlerFuncs to handle Add, Update, and Delete events on secrets. +// AddFunc: Invoked when a new secret is added. It logs the addition of the secret. +// UpdateFunc: Invoked when an existing secret is updated. It performs validation of the route's external certificate configuration. +// If the validation fails, it records the route rejection, and triggers the deletion of the route by calling the HandleRoute method with a watch.Deleted event. +// If the validation succeeds, it updates the route's TLS certificate and key with the new secret data and calls the next plugin's HandleRoute method with a watch.Modified event, and then the next plugin's Commit() method. +// DeleteFunc: Invoked when the secret is deleted. It unregisters the associated route, records the route rejection, and triggers the deletion of the route by calling the HandleRoute method with a watch.Deleted event. +func (p *RouteSecretManager) generateSecretHandler(route *routev1.Route) cache.ResourceEventHandlerFuncs { + // secret handler + return cache.ResourceEventHandlerFuncs{ + + // AddFunc is intentionally left empty (only logs the event) because this handler is generated only after ensuring the existence of the secret. + // By leaving this empty, we prevent unnecessary triggering for the addition of the secret again. Additionally, GetSecret() method is called + // immediately after registering with the secretManager, to read the secret from the cache. + AddFunc: func(obj interface{}) { + secret := obj.(*kapi.Secret) + log.V(4).Info("secret added for route", "namespace", route.Namespace, "secret", secret.Name, "route", route.Name) + }, + + UpdateFunc: func(old interface{}, new interface{}) { + secretOld := old.(*kapi.Secret) + secretNew := new.(*kapi.Secret) + log.V(4).Info("secret updated for route", "namespace", route.Namespace, "secret", secretNew.Name, "old-version", secretOld.ResourceVersion, "new-version", secretNew.ResourceVersion, "route", route.Name) + + // re-validate + fldPath := field.NewPath("spec").Child("tls").Child("externalCertificate") + if err := routeapihelpers.ValidateTLSExternalCertificate(route, fldPath, p.sarClient, p.secretsGetter).ToAggregate(); err != nil { + log.Error(err, "skipping route due to invalid externalCertificate configuration", "namespace", route.Namespace, "route", route.Name) + p.recorder.RecordRouteRejection(route, "ExternalCertificateValidationFailed", err.Error()) + p.plugin.HandleRoute(watch.Deleted, route) + return + } + + // read referenced secret (updated data) + secret, err := p.secretManager.GetSecret(context.TODO(), route.Namespace, route.Name) + if err != nil { + log.Error(err, "failed to get referenced secret") + p.recorder.RecordRouteRejection(route, "ExternalCertificateGetFailed", err.Error()) + p.plugin.HandleRoute(watch.Deleted, route) + return + } + + // update tls.Certificate and tls.Key + route.Spec.TLS.Certificate = string(secret.Data["tls.crt"]) + route.Spec.TLS.Key = string(secret.Data["tls.key"]) + + // call the next plugin with watch.Modified + p.plugin.HandleRoute(watch.Modified, route) + // commit the changes + p.plugin.Commit() + }, + + DeleteFunc: func(obj interface{}) { + secret := obj.(*kapi.Secret) + msg := fmt.Sprintf("secret %s deleted for route %s/%s", secret.Name, route.Namespace, route.Name) + log.V(4).Info(msg) + + // unregister associated secret monitor + if err := p.secretManager.UnregisterRoute(route.Namespace, route.Name); err != nil { + log.Error(err, "failed to unregister route") + } + + p.recorder.RecordRouteRejection(route, "ExternalCertificateSecretDeleted", msg) + p.plugin.HandleRoute(watch.Deleted, route) + }, + } +} + +func hasExternalCertificate(route *routev1.Route) bool { + tls := route.Spec.TLS + return tls != nil && tls.ExternalCertificate != nil && len(tls.ExternalCertificate.Name) > 0 +} diff --git a/pkg/router/controller/route_secret_manager_test.go b/pkg/router/controller/route_secret_manager_test.go new file mode 100644 index 000000000..bd0b99693 --- /dev/null +++ b/pkg/router/controller/route_secret_manager_test.go @@ -0,0 +1,1151 @@ +package controller + +import ( + "context" + "fmt" + "reflect" + "testing" + + "github.com/openshift/library-go/pkg/route/secretmanager/fake" + "github.com/openshift/router/pkg/router" + + routev1 "github.com/openshift/api/route/v1" + authorizationv1 "k8s.io/api/authorization/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/watch" + testclient "k8s.io/client-go/kubernetes/fake" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" +) + +type testSARCreator struct { + allow bool + err error + sar *authorizationv1.SubjectAccessReview +} + +func (t *testSARCreator) Create(_ context.Context, subjectAccessReview *authorizationv1.SubjectAccessReview, _ metav1.CreateOptions) (*authorizationv1.SubjectAccessReview, error) { + t.sar = subjectAccessReview + return &authorizationv1.SubjectAccessReview{ + Status: authorizationv1.SubjectAccessReviewStatus{ + Allowed: t.allow, + }, + }, t.err +} + +type testSecretGetter struct { + namespace string + secret *corev1.Secret +} + +func (t *testSecretGetter) Secrets(_ string) corev1client.SecretInterface { + return testclient.NewSimpleClientset(t.secret).CoreV1().Secrets(t.namespace) +} + +// fakeSecretInformer will list/watch only one secret inside a namespace +func fakeSecretInformer(fakeKubeClient *testclient.Clientset, namespace, name string) cache.SharedInformer { + fieldSelector := fields.OneTermEqualSelector("metadata.name", name).String() + return cache.NewSharedInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + options.FieldSelector = fieldSelector + return fakeKubeClient.CoreV1().Secrets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + options.FieldSelector = fieldSelector + return fakeKubeClient.CoreV1().Secrets(namespace).Watch(context.TODO(), options) + }, + }, + &corev1.Secret{}, + 0, + ) +} + +func fakeSecret(namespace, name string, secretType corev1.SecretType, data map[string][]byte) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Data: data, + Type: secretType, + } +} + +type fakePluginDone struct { + eventType watch.EventType + route *routev1.Route + err error + doneCh chan struct{} +} + +func (p *fakePluginDone) HandleRoute(eventType watch.EventType, route *routev1.Route) error { + defer close(p.doneCh) + p.eventType, p.route = eventType, route + return p.err +} +func (p *fakePluginDone) HandleNode(t watch.EventType, node *corev1.Node) error { + return fmt.Errorf("not expected") +} +func (p *fakePluginDone) HandleEndpoints(watch.EventType, *corev1.Endpoints) error { + return fmt.Errorf("not expected") +} +func (p *fakePluginDone) HandleNamespaces(namespaces sets.String) error { + return fmt.Errorf("not expected") +} +func (p *fakePluginDone) Commit() error { + return p.err +} + +var _ router.Plugin = &fakePluginDone{} + +func TestRouteSecretManager(t *testing.T) { + + scenarios := []struct { + name string + route *routev1.Route + secretManager fake.SecretManager + eventType watch.EventType + allow bool + expectedRoute *routev1.Route + expectedEventType watch.EventType + expectedRejections map[string]string + expectedError bool + }{ + // scenarios when route is added + { + name: "route added with externalCertificate denied", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{}), + }, + eventType: watch.Added, + allow: false, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route added with externalCertificate allowed but secret not found", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("other-sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{}), + }, + eventType: watch.Added, + allow: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route added with externalCertificate allowed but secret of incorrect type", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeBasicAuth, map[string][]byte{}), + }, + eventType: watch.Added, + allow: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route added with externalCertificate allowed and correct secret but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + Err: fmt.Errorf("something"), + }, + eventType: watch.Added, + allow: true, + expectedError: true, + }, + { + name: "route added with externalCertificate allowed and correct secret", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + }, + eventType: watch.Added, + allow: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + Certificate: "my-crt", + Key: "my-key", + }, + }, + }, + expectedEventType: watch.Added, + }, + { + name: "route added without externalCertificate", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + eventType: watch.Added, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + expectedEventType: watch.Added, + }, + + // scenarios when route is updated (old route without externalCertificate, new route with externalCertificate) + { + name: "route updated: old route without externalCertificate, new route with externalCertificate denied", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: false, + }, + allow: false, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route updated: old route without externalCertificate, new route with externalCertificate allowed but secret not found", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("other-sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: false, + }, + allow: true, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route updated: old route without externalCertificate, new route with externalCertificate allowed but secret of incorrect type", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeBasicAuth, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: false, + }, + allow: true, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route updated: old route without externalCertificate, new route with externalCertificate allowed and correct secret but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: false, + Err: fmt.Errorf("something"), + }, + allow: true, + eventType: watch.Modified, + expectedError: true, + }, + { + name: "route updated: old route without externalCertificate, new route with externalCertificate allowed and correct secret", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: false, + }, + allow: true, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + Certificate: "my-crt", + Key: "my-key", + }, + }, + }, + expectedEventType: watch.Modified, + }, + + // scenarios when route is updated (old route with externalCertificate, new route with externalCertificate) + { + name: "route updated: old route with externalCertificate, new route with externalCertificate denied", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: true, + }, + allow: false, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route updated: old route with externalCertificate, new route with externalCertificate allowed but secret not found", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("other-sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: true, + }, + allow: true, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route updated: old route with externalCertificate, new route with externalCertificate allowed but secret of incorrect type", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeBasicAuth, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: true, + }, + allow: true, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + expectedError: true, + }, + { + name: "route updated: old route with externalCertificate, new route with externalCertificate allowed and correct secret but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: true, + Err: fmt.Errorf("something"), + }, + allow: true, + eventType: watch.Modified, + expectedError: true, + }, + { + name: "route updated: old route with externalCertificate, new route with externalCertificate allowed and correct secret", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + IsRegistered: true, + }, + allow: true, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + Certificate: "my-crt", + Key: "my-key", + }, + }, + }, + expectedEventType: watch.Modified, + }, + + // scenarios when route is updated (old route with externalCertificate, new route without externalCertificate) + { + name: "route updated: old route with externalCertificate, new route without externalCertificate but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + secretManager: fake.SecretManager{ + IsRegistered: true, + Err: fmt.Errorf("something"), + }, + eventType: watch.Modified, + expectedError: true, + }, + { + name: "route updated: old route with externalCertificate, new route without externalCertificate: works", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + secretManager: fake.SecretManager{ + IsRegistered: true, + }, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + expectedEventType: watch.Modified, + }, + + // scenario when route is updated (old route without externalCertificate, new route without externalCertificate) + { + name: "route updated: old route without externalCertificate, new route without externalCertificate", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + secretManager: fake.SecretManager{ + IsRegistered: false, + }, + eventType: watch.Modified, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + expectedEventType: watch.Modified, + }, + + // scenarios when route is deleted + { + name: "route deleted without externalCertificate registered", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + secretManager: fake.SecretManager{IsRegistered: false}, + eventType: watch.Deleted, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{}, + }, + expectedEventType: watch.Deleted, + }, + { + name: "route deleted with externalCertificate registered", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{IsRegistered: true}, + eventType: watch.Deleted, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + }, + { + name: "route deleted with externalCertificate registered, but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{IsRegistered: true, Err: fmt.Errorf("something")}, + eventType: watch.Deleted, + expectedError: true, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + p := &fakePlugin{} + recorder := routeStatusRecorder{rejections: make(map[string]string)} + + // assign default value to expectedRejections + if s.expectedRejections == nil { + s.expectedRejections = map[string]string{} + } + rsm := NewRouteSecretManager(p, recorder, &s.secretManager, &testSecretGetter{namespace: s.route.Namespace, secret: s.secretManager.Secret}, &testSARCreator{allow: s.allow}) + + gotErr := rsm.HandleRoute(s.eventType, s.route) + if (gotErr != nil) != s.expectedError { + t.Fatalf("expected error to be %t, but got %t", s.expectedError, gotErr != nil) + } + if !reflect.DeepEqual(s.expectedRoute, p.route) { + t.Fatalf("expected route for next plugin %v, but got %v", s.expectedRoute, p.route) + } + if s.expectedEventType != p.t { + t.Fatalf("expected %s event for next plugin, but got %s", s.expectedEventType, p.t) + } + if !reflect.DeepEqual(s.expectedRejections, recorder.rejections) { + t.Fatalf("expected rejections %v, but got %v", s.expectedRejections, recorder.rejections) + } + }) + } +} + +func TestSecretUpdateAndDelete(t *testing.T) { + + scenarios := []struct { + name string + route *routev1.Route + secretManager fake.SecretManager + allow bool + deleteSecret bool + expectedRoute *routev1.Route + expectedEventType watch.EventType + expectedRejections map[string]string + }{ + { + name: "secret updated but permission revoked", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + }, + allow: false, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateValidationFailed", + }, + }, + { + name: "secret updated with permission but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + Err: fmt.Errorf("something"), + }, + allow: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateGetFailed", + }, + }, + { + name: "secret updated with permission correctly", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + }, + allow: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + Certificate: "my-crt", + Key: "my-key", + }, + }, + }, + expectedEventType: watch.Modified, + expectedRejections: map[string]string{}, + }, + { + name: "secret deleted but got error from secretManager", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + Err: fmt.Errorf("something"), + }, + deleteSecret: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateSecretDeleted", + }, + }, + { + name: "secret deleted and route successfully unregistered", + route: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + secretManager: fake.SecretManager{ + Secret: fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{ + "tls.crt": []byte("my-crt"), + "tls.key": []byte("my-key"), + }), + }, + deleteSecret: true, + expectedRoute: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-test", + Namespace: "sandbox", + }, + Spec: routev1.RouteSpec{ + TLS: &routev1.TLSConfig{ + ExternalCertificate: &routev1.LocalObjectReference{ + Name: "tls-secret", + }, + }, + }, + }, + expectedEventType: watch.Deleted, + expectedRejections: map[string]string{ + "sandbox-route-test": "ExternalCertificateSecretDeleted", + }, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + oldSecret := fakeSecret("sandbox", "tls-secret", corev1.SecretTypeTLS, map[string][]byte{}) + kubeClient := testclient.NewSimpleClientset(oldSecret) + informer := fakeSecretInformer(kubeClient, "sandbox", "tls-secret") + go informer.Run(context.TODO().Done()) + + // wait for informer to start + if !cache.WaitForCacheSync(context.TODO().Done(), informer.HasSynced) { + t.Fatal("cache not synced yet") + } + + p := &fakePluginDone{ + doneCh: make(chan struct{}), + } + recorder := routeStatusRecorder{rejections: make(map[string]string)} + rsm := NewRouteSecretManager(p, recorder, &s.secretManager, &testSecretGetter{namespace: s.route.Namespace, secret: oldSecret}, &testSARCreator{allow: s.allow}) + + if _, err := informer.AddEventHandler(rsm.generateSecretHandler(s.route)); err != nil { + t.Fatalf("failed to add handler: %v", err) + } + + if s.deleteSecret { + // delete the secret + if err := kubeClient.CoreV1().Secrets(s.route.Namespace).Delete(context.TODO(), s.secretManager.Secret.Name, metav1.DeleteOptions{}); err != nil { + t.Fatalf("failed to delete secret: %v", err) + } + + } else { + // update the secret + if _, err := kubeClient.CoreV1().Secrets(s.route.Namespace).Update(context.TODO(), s.secretManager.Secret, metav1.UpdateOptions{}); err != nil { + t.Fatalf("failed to update secret: %v", err) + } + } + // wait until p.plugin.HandleRoute() completes (required to handle race condition) + <-p.doneCh + + if !reflect.DeepEqual(s.expectedRoute, p.route) { + t.Fatalf("expected route for next plugin %v, but got %v", s.expectedRoute, p.route) + } + if s.expectedEventType != p.eventType { + t.Fatalf("expected %s event for next plugin, but got %s", s.expectedEventType, p.eventType) + } + if !reflect.DeepEqual(s.expectedRejections, recorder.rejections) { + t.Fatalf("expected rejections %v, but got %v", s.expectedRejections, recorder.rejections) + } + }) + } +} diff --git a/pkg/router/routeapihelpers/validation.go b/pkg/router/routeapihelpers/validation.go index 863054b88..2f105d3e8 100644 --- a/pkg/router/routeapihelpers/validation.go +++ b/pkg/router/routeapihelpers/validation.go @@ -2,6 +2,7 @@ package routeapihelpers import ( "bytes" + "context" "crypto/ecdsa" "crypto/rsa" "crypto/tls" @@ -9,10 +10,25 @@ import ( "encoding/pem" "fmt" - "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apiserver/pkg/authentication/user" "k8s.io/client-go/util/cert" routev1 "github.com/openshift/api/route/v1" + "github.com/openshift/library-go/pkg/authorization/authorizationutil" + + authorizationv1 "k8s.io/api/authorization/v1" + kapi "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +const ( + // routerServiceAccount is used to validate RBAC permissions for externalCertificate + // TODO: avoid hard coding the serviceaccount name, and instead use environment variable, may be through cluster-ingress-operator. + routerServiceAccount = "system:serviceaccount:openshift-ingress:router" ) type blockVerifierFunc func(block *pem.Block) (*pem.Block, error) @@ -390,3 +406,58 @@ func UpgradeRouteValidation(route *routev1.Route) field.ErrorList { // are getting rejected now). return nil } + +// ValidateTLSExternalCertificate tests different pre-conditions required for +// using externalCertificate. +func ValidateTLSExternalCertificate(route *routev1.Route, fldPath *field.Path, sarc authorizationclient.SubjectAccessReviewInterface, secretsGetter corev1client.SecretsGetter) field.ErrorList { + tls := route.Spec.TLS + + errs := field.ErrorList{} + // The router serviceaccount must have permission to get/list/watch the referenced secret. + // The role and rolebinding to provide this access must be provided by the user. + if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount}, + &authorizationv1.ResourceAttributes{ + Namespace: route.Namespace, + Verb: "get", + Resource: "secrets", + Name: tls.ExternalCertificate.Name, + }); err != nil { + errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to get this secret")) + } + + if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount}, + &authorizationv1.ResourceAttributes{ + Namespace: route.Namespace, + Verb: "watch", + Resource: "secrets", + Name: tls.ExternalCertificate.Name, + }); err != nil { + errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to watch this secret")) + } + + if err := authorizationutil.Authorize(sarc, &user.DefaultInfo{Name: routerServiceAccount}, + &authorizationv1.ResourceAttributes{ + Namespace: route.Namespace, + Verb: "list", + Resource: "secrets", + Name: tls.ExternalCertificate.Name, + }); err != nil { + errs = append(errs, field.Forbidden(fldPath, "router serviceaccount does not have permission to list this secret")) + } + + // The secret should be in the same namespace as that of the route. + secret, err := secretsGetter.Secrets(route.Namespace).Get(context.TODO(), tls.ExternalCertificate.Name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return append(errs, field.NotFound(fldPath, err.Error())) + } + return append(errs, field.InternalError(fldPath, err)) + } + + // The secret should be of type kubernetes.io/tls + if secret.Type != kapi.SecretTypeTLS { + errs = append(errs, field.Invalid(fldPath, tls.ExternalCertificate.Name, fmt.Sprintf("secret of type %q required", kapi.SecretTypeTLS))) + } + + return errs +} diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 19e78e213..1b747224e 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -14,6 +14,7 @@ import ( "time" routev1 "github.com/openshift/api/route/v1" + fakesm "github.com/openshift/library-go/pkg/route/secretmanager/fake" projectfake "github.com/openshift/client-go/project/clientset/versioned/fake" routeclient "github.com/openshift/client-go/route/clientset/versioned" @@ -160,6 +161,7 @@ u3YLAbyW/lHhOCiZu2iAI8AbmXem9lW6Tr7p/97s0w== Value: `'"shouldn'\''t break"'`, Action: "Set", }}, + SecretManager: &fakesm.SecretManager{}, } plugin, err = templateplugin.NewTemplatePlugin(pluginCfg, svcFetcher) if err != nil { diff --git a/pkg/router/template/fake.go b/pkg/router/template/fake.go index 614db31a7..2f4560772 100644 --- a/pkg/router/template/fake.go +++ b/pkg/router/template/fake.go @@ -1,5 +1,9 @@ package templaterouter +import ( + fakesm "github.com/openshift/library-go/pkg/route/secretmanager/fake" +) + // NewFakeTemplateRouter provides an empty template router with a simple certificate manager // backed by a fake cert writer for testing func NewFakeTemplateRouter() *templateRouter { @@ -9,6 +13,7 @@ func NewFakeTemplateRouter() *templateRouter { serviceUnits: make(map[ServiceUnitKey]ServiceUnit), certManager: fakeCertManager, rateLimitedCommitFunction: nil, + secretManager: &fakesm.SecretManager{}, } } diff --git a/pkg/router/template/plugin.go b/pkg/router/template/plugin.go index d1633720d..8d592ca28 100644 --- a/pkg/router/template/plugin.go +++ b/pkg/router/template/plugin.go @@ -17,6 +17,7 @@ import ( routev1 "github.com/openshift/api/route/v1" + "github.com/openshift/library-go/pkg/route/secretmanager" unidlingapi "github.com/openshift/router/pkg/router/unidling" ) @@ -68,6 +69,7 @@ type TemplatePluginConfig struct { HTTPHeaderNameCaseAdjustments []HTTPHeaderNameCaseAdjustment HTTPResponseHeaders []HTTPHeader HTTPRequestHeaders []HTTPHeader + SecretManager secretmanager.SecretManager } // RouterInterface controls the interaction of the plugin with the underlying router implementation @@ -164,6 +166,7 @@ func NewTemplatePlugin(cfg TemplatePluginConfig, lookupSvc ServiceLookup) (*Temp httpHeaderNameCaseAdjustments: cfg.HTTPHeaderNameCaseAdjustments, httpResponseHeaders: cfg.HTTPResponseHeaders, httpRequestHeaders: cfg.HTTPRequestHeaders, + secretManager: cfg.SecretManager, } router, err := newTemplateRouter(templateRouterCfg) return newDefaultTemplatePlugin(router, cfg.IncludeUDP, lookupSvc), err diff --git a/pkg/router/template/router.go b/pkg/router/template/router.go index e83363f62..17ccccb9f 100644 --- a/pkg/router/template/router.go +++ b/pkg/router/template/router.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" routev1 "github.com/openshift/api/route/v1" + "github.com/openshift/library-go/pkg/route/secretmanager" logf "github.com/openshift/router/log" "github.com/openshift/router/pkg/router/crl" @@ -64,6 +65,7 @@ type templateRouter struct { state map[ServiceAliasConfigKey]ServiceAliasConfig serviceUnits map[ServiceUnitKey]ServiceUnit certManager certificateManager + secretManager secretmanager.SecretManager // defaultCertificate is a concatenated certificate(s), their keys, and their CAs that should be used by the underlying // implementation as the default certificate if no certificate is resolved by the normal matching mechanisms. This is // usually a wildcard certificate for a cloud domain such as *.mypaas.com to allow applications to create app.mypaas.com @@ -155,6 +157,7 @@ type templateRouterCfg struct { httpHeaderNameCaseAdjustments []HTTPHeaderNameCaseAdjustment httpResponseHeaders []HTTPHeader httpRequestHeaders []HTTPHeader + secretManager secretmanager.SecretManager } // templateConfig is a subset of the templateRouter information that should be passed to the template for generating @@ -253,6 +256,7 @@ func newTemplateRouter(cfg templateRouterCfg) (*templateRouter, error) { state: make(map[ServiceAliasConfigKey]ServiceAliasConfig), serviceUnits: make(map[ServiceUnitKey]ServiceUnit), certManager: certManager, + secretManager: cfg.secretManager, defaultCertificate: cfg.defaultCertificate, defaultCertificatePath: cfg.defaultCertificatePath, defaultCertificateDir: cfg.defaultCertificateDir, @@ -1130,6 +1134,14 @@ func (r *templateRouter) RemoveRoute(route *routev1.Route) { defer r.lock.Unlock() r.removeRouteInternal(route) + + if r.secretManager != nil { // TODO: remove this nil check while dropping AllowExternalCertificates flag. + if r.secretManager.IsRouteRegistered(route.Namespace, route.Name) { + if err := r.secretManager.UnregisterRoute(route.Namespace, route.Name); err != nil { + log.Error(err, "failed to unregister route") + } + } + } } // removeRouteInternal removes the given route - internal diff --git a/pkg/router/template/router_test.go b/pkg/router/template/router_test.go index 9c4610d11..7266957bb 100644 --- a/pkg/router/template/router_test.go +++ b/pkg/router/template/router_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-cmp/cmp" routev1 "github.com/openshift/api/route/v1" + fakesm "github.com/openshift/library-go/pkg/route/secretmanager/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" @@ -627,6 +628,7 @@ func findCert(cert string, certs map[string]Certificate, isPrivateKey bool, t *t // TestRemoveRoute tests removing a ServiceAliasConfig from a ServiceUnit func TestRemoveRoute(t *testing.T) { router := NewFakeTemplateRouter() + router.secretManager = &fakesm.SecretManager{IsRegistered: true} route := &routev1.Route{ ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", diff --git a/vendor/github.com/openshift/api/route/.codegen.yaml b/vendor/github.com/openshift/api/route/.codegen.yaml index d2791f7b5..65cf5d814 100644 --- a/vendor/github.com/openshift/api/route/.codegen.yaml +++ b/vendor/github.com/openshift/api/route/.codegen.yaml @@ -1,8 +1,3 @@ schemapatch: - requiredFeatureSets: - - '' - - 'Default' - - 'TechPreviewNoUpgrade' - - 'CustomNoUpgrade' swaggerdocs: commentPolicy: Warn diff --git a/vendor/github.com/openshift/api/route/v1/custom.route.testsuite.yaml b/vendor/github.com/openshift/api/route/v1/custom.route.testsuite.yaml deleted file mode 100644 index 4a8042fc1..000000000 --- a/vendor/github.com/openshift/api/route/v1/custom.route.testsuite.yaml +++ /dev/null @@ -1,103 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this -name: '[CustomNoUpgrade] Route' -crd: route-CustomNoUpgrade.crd.yaml -tests: - onCreate: - - name: Should be able to create a minimal Route - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - weight: 100 - wildcardPolicy: None - - name: 'cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Allow - expectedError: 'cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow' - - name: 'spec.tls.termination: passthrough is compatible with spec.tls.insecureEdgeTerminationPolicy: Redirect' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Redirect - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - weight: 100 - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Redirect - wildcardPolicy: None - - name: 'spec.tls.termination: passthrough is compatible with spec.tls.insecureEdgeTerminationPolicy: None' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: None - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - weight: 100 - tls: - termination: passthrough - insecureEdgeTerminationPolicy: None - wildcardPolicy: None - - name: 'cannot have both spec.tls.certificate and spec.tls.externalCertificate' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - tls: - termination: edge - key: |- - -----BEGIN RSA PRIVATE KEY----- - -----END RSA PRIVATE KEY----- - certificate: |- - -----BEGIN CERTIFICATE----- - -----END CERTIFICATE----- - externalCertificate: - name: "my-local-secret" - expectedError: 'Invalid value: "object": cannot have both spec.tls.certificate and spec.tls.externalCertificate' diff --git a/vendor/github.com/openshift/api/route/v1/generated.proto b/vendor/github.com/openshift/api/route/v1/generated.proto index 66b35420e..1797fe770 100644 --- a/vendor/github.com/openshift/api/route/v1/generated.proto +++ b/vendor/github.com/openshift/api/route/v1/generated.proto @@ -404,7 +404,7 @@ message RouterShard { // TLSConfig defines config used to secure a route and provide termination // // +kubebuilder:validation:XValidation:rule="has(self.termination) && has(self.insecureEdgeTerminationPolicy) ? !((self.termination=='passthrough') && (self.insecureEdgeTerminationPolicy=='Allow')) : true", message="cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow" -// +openshift:validation:FeatureSetAwareXValidation:featureSet=TechPreviewNoUpgrade;CustomNoUpgrade,rule="!(has(self.certificate) && has(self.externalCertificate))", message="cannot have both spec.tls.certificate and spec.tls.externalCertificate" +// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalRouteCertificate,rule="!(has(self.certificate) && has(self.externalCertificate))", message="cannot have both spec.tls.certificate and spec.tls.externalCertificate" message TLSConfig { // termination indicates termination type. // @@ -449,7 +449,7 @@ message TLSConfig { // be present in the same namespace as that of the Route. // Forbidden when `certificate` is set. // - // +openshift:enable:FeatureSets=CustomNoUpgrade;TechPreviewNoUpgrade + // +openshift:enable:FeatureGate=ExternalRouteCertificate // +optional optional LocalObjectReference externalCertificate = 7; } diff --git a/vendor/github.com/openshift/api/route/v1/route-CustomNoUpgrade.crd.yaml b/vendor/github.com/openshift/api/route/v1/route-CustomNoUpgrade.crd.yaml deleted file mode 100644 index d46b07910..000000000 --- a/vendor/github.com/openshift/api/route/v1/route-CustomNoUpgrade.crd.yaml +++ /dev/null @@ -1,618 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.openshift.io: https://github.com/openshift/api/pull/1228 - release.openshift.io/feature-set: CustomNoUpgrade - name: routes.route.openshift.io -spec: - group: route.openshift.io - names: - kind: Route - plural: routes - singular: route - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ingress[0].host - name: Host - type: string - - jsonPath: .status.ingress[0].conditions[?(@.type=="Admitted")].status - name: Admitted - type: string - - jsonPath: .spec.to.name - name: Service - type: string - - jsonPath: .spec.tls.type - name: TLS - type: string - name: v1 - schema: - openAPIV3Schema: - description: "A route allows developers to expose services through an HTTP(S) - aware load balancing and proxy layer via a public DNS entry. The route may - further specify TLS options and a certificate, or specify a public CNAME - that the router should also accept for HTTP and HTTPS traffic. An administrator - typically configures their router to be visible outside the cluster firewall, - and may also add additional security, caching, or traffic controls on the - service content. Routers usually talk directly to the service endpoints. - \n Once a route is created, the `host` field may not be changed. Generally, - routers use the oldest route with a given host when resolving conflicts. - \n Routers are subject to additional customization and may support additional - controls via the annotations field. \n Because administrators may configure - multiple routers, the route status field is used to return information to - clients about the names and states of the route under each router. If a - client chooses a duplicate name, for instance, the route status conditions - are used to indicate the route cannot be chosen. \n To enable HTTP/2 ALPN - on a route it requires a custom (non-wildcard) certificate. This prevents - connection coalescing by clients, notably web browsers. We do not support - HTTP/2 ALPN on routes that use the default certificate because of the risk - of connection re-use/coalescing. Routes that do not have their own custom - certificate will not be HTTP/2 ALPN-enabled on either the frontend or the - backend. \n Compatibility level 1: Stable within a major release for a minimum - of 12 months or 3 minor releases (whichever is longer)." - 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 is the desired state of the route - properties: - alternateBackends: - description: alternateBackends allows up to 3 additional backends - to be assigned to the route. Only the Service kind is allowed, and - it will be defaulted to Service. Use the weight field in RouteTargetReference - object to specify relative preference. - items: - description: RouteTargetReference specifies the target that resolve - into endpoints. Only the 'Service' kind is allowed. Use 'weight' - field to emphasize one over others. - properties: - kind: - default: Service - description: The kind of target that the route is referring - to. Currently, only 'Service' is allowed - enum: - - Service - - "" - type: string - name: - description: name of the service/target that is being referred - to. e.g. name of the service - minLength: 1 - type: string - weight: - default: 100 - description: weight as an integer between 0 and 256, default - 100, that specifies the target's relative weight against other - target reference objects. 0 suppresses requests to this backend. - format: int32 - maximum: 256 - minimum: 0 - type: integer - required: - - kind - - name - type: object - maxItems: 3 - type: array - host: - description: host is an alias/DNS that points to the service. Optional. - If not specified a route name will typically be automatically chosen. - Must follow DNS952 subdomain conventions. - maxLength: 253 - pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ - type: string - httpHeaders: - description: httpHeaders defines policy for HTTP headers. - properties: - actions: - description: 'actions specifies options for modifying headers - and their values. Note that this option only applies to cleartext - HTTP connections and to secure HTTP connections for which the - ingress controller terminates encryption (that is, edge-terminated - or reencrypt connections). Headers cannot be modified for TLS - passthrough connections. Setting the HSTS (`Strict-Transport-Security`) - header is not supported via actions. `Strict-Transport-Security` - may only be configured using the "haproxy.router.openshift.io/hsts_header" - route annotation, and only in accordance with the policy specified - in Ingress.Spec.RequiredHSTSPolicies. In case of HTTP request - headers, the actions specified in spec.httpHeaders.actions on - the Route will be executed after the actions specified in the - IngressController''s spec.httpHeaders.actions field. In case - of HTTP response headers, the actions specified in spec.httpHeaders.actions - on the IngressController will be executed after the actions - specified in the Route''s spec.httpHeaders.actions field. The - headers set via this API will not appear in access logs. Any - actions defined here are applied after any actions related to - the following other fields: cache-control, spec.clientTLS, spec.httpHeaders.forwardedHeaderPolicy, - spec.httpHeaders.uniqueId, and spec.httpHeaders.headerNameCaseAdjustments. - The following header names are reserved and may not be modified - via this API: Strict-Transport-Security, Proxy, Cookie, Set-Cookie. - Note that the total size of all net added headers *after* interpolating - dynamic values must not exceed the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. Please refer to the documentation - for that API field for more details.' - properties: - request: - description: 'request is a list of HTTP request headers to - modify. Currently, actions may define to either `Set` or - `Delete` headers values. Actions defined here will modify - the request headers of all requests made through a route. - These actions are applied to a specific Route defined within - a cluster i.e. connections made through a route. Currently, - actions may define to either `Set` or `Delete` headers values. - Route actions will be executed after IngressController actions - for request headers. Actions are applied in sequence as - defined in this list. A maximum of 20 request header actions - may be configured. You can use this field to specify HTTP - request headers that should be set or deleted when forwarding - connections from the client to your application. Sample - fetchers allowed are "req.hdr" and "ssl_c_der". Converters - allowed are "lower" and "base64". Example header values: - "%[req.hdr(X-target),lower]", "%{+Q}[ssl_c_der,base64]". - Any request header configuration applied directly via a - Route resource using this API will override header configuration - for a header of the same name applied via spec.httpHeaders.actions - on the IngressController or route annotation. Note: This - field cannot be used if your route uses TLS passthrough.' - items: - description: RouteHTTPHeader specifies configuration for - setting or deleting an HTTP header. - properties: - action: - description: action specifies actions to perform on - headers, such as setting or deleting headers. - properties: - set: - description: 'set defines the HTTP header that should - be set: added if it doesn''t exist or replaced - if it does. This field is required when type is - Set and forbidden otherwise.' - properties: - value: - description: value specifies a header value. - Dynamic values can be added. The value will - be interpreted as an HAProxy format string - as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise - must be a valid HTTP header value as defined - in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - The value of this field must be no more than - 16384 characters in length. Note that the - total size of all net added headers *after* - interpolating dynamic values must not exceed - the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. - maxLength: 16384 - minLength: 1 - type: string - required: - - value - type: object - type: - description: type defines the type of the action - to be applied on the header. Possible values are - Set or Delete. Set allows you to set HTTP request - and response headers. Delete allows you to delete - HTTP request and response headers. - enum: - - Set - - Delete - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: set is required when type is Set, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Set'' ? has(self.set) - : !has(self.set)' - name: - description: 'name specifies the name of a header on - which to perform an action. Its value must be a valid - HTTP header name as defined in RFC 2616 section 4.2. - The name must consist only of alphanumeric and the - following special characters, "-!#$%&''*+.^_`". The - following header names are reserved and may not be - modified via this API: Strict-Transport-Security, - Proxy, Cookie, Set-Cookie. It must be no more than - 255 characters in length. Header name must be unique.' - maxLength: 255 - minLength: 1 - pattern: ^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$ - type: string - x-kubernetes-validations: - - message: strict-transport-security header may not - be modified via header actions - rule: self.lowerAscii() != 'strict-transport-security' - - message: proxy header may not be modified via header - actions - rule: self.lowerAscii() != 'proxy' - - message: cookie header may not be modified via header - actions - rule: self.lowerAscii() != 'cookie' - - message: set-cookie header may not be modified via - header actions - rule: self.lowerAscii() != 'set-cookie' - required: - - action - - name - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: Either the header value provided is not in correct - format or the sample fetcher/converter specified is not - allowed. The dynamic header value will be interpreted - as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise must be - a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - Sample fetchers allowed are req.hdr, ssl_c_der. Converters - allowed are lower, base64. - rule: self.all(key, key.action.type == "Delete" || (has(key.action.set) - && key.action.set.value.matches('^(?:%(?:%|(?:\\{[-+]?[QXE](?:,[-+]?[QXE])*\\})?\\[(?:req\\.hdr\\([0-9A-Za-z-]+\\)|ssl_c_der)(?:,(?:lower|base64))*\\])|[^%[:cntrl:]])+$'))) - response: - description: 'response is a list of HTTP response headers - to modify. Currently, actions may define to either `Set` - or `Delete` headers values. Actions defined here will modify - the response headers of all requests made through a route. - These actions are applied to a specific Route defined within - a cluster i.e. connections made through a route. Route actions - will be executed before IngressController actions for response - headers. Actions are applied in sequence as defined in this - list. A maximum of 20 response header actions may be configured. - You can use this field to specify HTTP response headers - that should be set or deleted when forwarding responses - from your application to the client. Sample fetchers allowed - are "res.hdr" and "ssl_c_der". Converters allowed are "lower" - and "base64". Example header values: "%[res.hdr(X-target),lower]", - "%{+Q}[ssl_c_der,base64]". Note: This field cannot be used - if your route uses TLS passthrough.' - items: - description: RouteHTTPHeader specifies configuration for - setting or deleting an HTTP header. - properties: - action: - description: action specifies actions to perform on - headers, such as setting or deleting headers. - properties: - set: - description: 'set defines the HTTP header that should - be set: added if it doesn''t exist or replaced - if it does. This field is required when type is - Set and forbidden otherwise.' - properties: - value: - description: value specifies a header value. - Dynamic values can be added. The value will - be interpreted as an HAProxy format string - as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise - must be a valid HTTP header value as defined - in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - The value of this field must be no more than - 16384 characters in length. Note that the - total size of all net added headers *after* - interpolating dynamic values must not exceed - the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. - maxLength: 16384 - minLength: 1 - type: string - required: - - value - type: object - type: - description: type defines the type of the action - to be applied on the header. Possible values are - Set or Delete. Set allows you to set HTTP request - and response headers. Delete allows you to delete - HTTP request and response headers. - enum: - - Set - - Delete - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: set is required when type is Set, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Set'' ? has(self.set) - : !has(self.set)' - name: - description: 'name specifies the name of a header on - which to perform an action. Its value must be a valid - HTTP header name as defined in RFC 2616 section 4.2. - The name must consist only of alphanumeric and the - following special characters, "-!#$%&''*+.^_`". The - following header names are reserved and may not be - modified via this API: Strict-Transport-Security, - Proxy, Cookie, Set-Cookie. It must be no more than - 255 characters in length. Header name must be unique.' - maxLength: 255 - minLength: 1 - pattern: ^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$ - type: string - x-kubernetes-validations: - - message: strict-transport-security header may not - be modified via header actions - rule: self.lowerAscii() != 'strict-transport-security' - - message: proxy header may not be modified via header - actions - rule: self.lowerAscii() != 'proxy' - - message: cookie header may not be modified via header - actions - rule: self.lowerAscii() != 'cookie' - - message: set-cookie header may not be modified via - header actions - rule: self.lowerAscii() != 'set-cookie' - required: - - action - - name - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: Either the header value provided is not in correct - format or the sample fetcher/converter specified is not - allowed. The dynamic header value will be interpreted - as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise must be - a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - Sample fetchers allowed are res.hdr, ssl_c_der. Converters - allowed are lower, base64. - rule: self.all(key, key.action.type == "Delete" || (has(key.action.set) - && key.action.set.value.matches('^(?:%(?:%|(?:\\{[-+]?[QXE](?:,[-+]?[QXE])*\\})?\\[(?:res\\.hdr\\([0-9A-Za-z-]+\\)|ssl_c_der)(?:,(?:lower|base64))*\\])|[^%[:cntrl:]])+$'))) - type: object - type: object - path: - description: path that the router watches for, to route traffic for - to the service. Optional - pattern: ^/ - type: string - port: - description: If specified, the port to be used by the router. Most - routers will use all endpoints exposed by the service by default - - set this value to instruct routers which port to use. - properties: - targetPort: - anyOf: - - type: integer - - type: string - description: The target port on pods selected by the service this - route points to. If this is a string, it will be looked up as - a named port in the target endpoints port list. Required - x-kubernetes-int-or-string: true - required: - - targetPort - type: object - subdomain: - description: "subdomain is a DNS subdomain that is requested within - the ingress controller's domain (as a subdomain). If host is set - this field is ignored. An ingress controller may choose to ignore - this suggested name, in which case the controller will report the - assigned name in the status.ingress array or refuse to admit the - route. If this value is set and the server does not support this - field host will be populated automatically. Otherwise host is left - empty. The field may have multiple parts separated by a dot, but - not all ingress controllers may honor the request. This field may - not be changed after creation except by a user with the update routes/custom-host - permission. \n Example: subdomain `frontend` automatically receives - the router subdomain `apps.mycluster.com` to have a full hostname - `frontend.apps.mycluster.com`." - maxLength: 253 - pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ - type: string - tls: - description: The tls field provides the ability to configure certificates - and termination for the route. - properties: - caCertificate: - description: caCertificate provides the cert authority certificate - contents - type: string - certificate: - description: certificate provides certificate contents. This should - be a single serving certificate, not a certificate chain. Do - not include a CA certificate. - type: string - destinationCACertificate: - description: destinationCACertificate provides the contents of - the ca certificate of the final destination. When using reencrypt - termination this file should be provided in order to have routers - use it for health checks on the secure connection. If this field - is not specified, the router may provide its own destination - CA and perform hostname validation using the short service name - (service.namespace.svc), which allows infrastructure generated - certificates to automatically verify. - type: string - externalCertificate: - description: externalCertificate provides certificate contents - as a secret reference. This should be a single serving certificate, - not a certificate chain. Do not include a CA certificate. The - secret referenced should be present in the same namespace as - that of the Route. Forbidden when `certificate` is set. - properties: - name: - description: 'name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - type: object - x-kubernetes-map-type: atomic - insecureEdgeTerminationPolicy: - description: "insecureEdgeTerminationPolicy indicates the desired - behavior for insecure connections to a route. While each router - may make its own decisions on which ports to expose, this is - normally port 80. \n * Allow - traffic is sent to the server - on the insecure port (edge/reencrypt terminations only) (default). - * None - no traffic is allowed on the insecure port. * Redirect - - clients are redirected to the secure port." - enum: - - Allow - - None - - Redirect - - "" - type: string - key: - description: key provides key file contents - type: string - termination: - description: "termination indicates termination type. \n * edge - - TLS termination is done by the router and http is used to - communicate with the backend (default) * passthrough - Traffic - is sent straight to the destination without the router providing - TLS termination * reencrypt - TLS termination is done by the - router and https is used to communicate with the backend \n - Note: passthrough termination is incompatible with httpHeader - actions" - enum: - - edge - - reencrypt - - passthrough - type: string - required: - - termination - type: object - x-kubernetes-validations: - - message: cannot have both spec.tls.certificate and spec.tls.externalCertificate - rule: '!(has(self.certificate) && has(self.externalCertificate))' - - message: 'cannot have both spec.tls.termination: passthrough and - spec.tls.insecureEdgeTerminationPolicy: Allow' - rule: 'has(self.termination) && has(self.insecureEdgeTerminationPolicy) - ? !((self.termination==''passthrough'') && (self.insecureEdgeTerminationPolicy==''Allow'')) - : true' - to: - description: to is an object the route should use as the primary backend. - Only the Service kind is allowed, and it will be defaulted to Service. - If the weight field (0-256 default 100) is set to zero, no traffic - will be sent to this backend. - properties: - kind: - default: Service - description: The kind of target that the route is referring to. - Currently, only 'Service' is allowed - enum: - - Service - - "" - type: string - name: - description: name of the service/target that is being referred - to. e.g. name of the service - minLength: 1 - type: string - weight: - default: 100 - description: weight as an integer between 0 and 256, default 100, - that specifies the target's relative weight against other target - reference objects. 0 suppresses requests to this backend. - format: int32 - maximum: 256 - minimum: 0 - type: integer - required: - - kind - - name - type: object - wildcardPolicy: - default: None - description: Wildcard policy if any for the route. Currently only - 'Subdomain' or 'None' is allowed. - enum: - - None - - Subdomain - - "" - type: string - required: - - to - type: object - x-kubernetes-validations: - - message: header actions are not permitted when tls termination is passthrough. - rule: '!has(self.tls) || self.tls.termination != ''passthrough'' || - !has(self.httpHeaders)' - status: - description: status is the current state of the route - properties: - ingress: - description: ingress describes the places where the route may be exposed. - The list of ingress points may contain duplicate Host or RouterName - values. Routes are considered live once they are `Ready` - items: - description: RouteIngress holds information about the places where - a route is exposed. - properties: - conditions: - description: Conditions is the state of the route, may be empty. - items: - description: RouteIngressCondition contains details for the - current condition of this route on a particular router. - properties: - lastTransitionTime: - description: RFC 3339 date and time when this condition - last transitioned - format: date-time - type: string - message: - description: Human readable message indicating details - about last transition. - type: string - reason: - description: (brief) reason for the condition's last transition, - and is usually a machine and human readable constant - type: string - status: - description: Status is the status of the condition. Can - be True, False, Unknown. - type: string - type: - description: Type is the type of the condition. Currently - only Admitted or UnservableInFutureVersions. - type: string - required: - - status - - type - type: object - type: array - host: - description: Host is the host string under which the route is - exposed; this value is required - type: string - routerCanonicalHostname: - description: CanonicalHostname is the external host name for - the router that can be used as a CNAME for the host requested - for this route. This value is optional and may not be set - in all cases. - type: string - routerName: - description: Name is a name chosen by the router to identify - itself; this value is required - type: string - wildcardPolicy: - description: Wildcard policy is the wildcard policy that was - allowed where this route is exposed. - type: string - type: object - type: array - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/vendor/github.com/openshift/api/route/v1/route-TechPreviewNoUpgrade.crd.yaml b/vendor/github.com/openshift/api/route/v1/route-TechPreviewNoUpgrade.crd.yaml deleted file mode 100644 index 48ff2c5e8..000000000 --- a/vendor/github.com/openshift/api/route/v1/route-TechPreviewNoUpgrade.crd.yaml +++ /dev/null @@ -1,618 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.openshift.io: https://github.com/openshift/api/pull/1228 - release.openshift.io/feature-set: TechPreviewNoUpgrade - name: routes.route.openshift.io -spec: - group: route.openshift.io - names: - kind: Route - plural: routes - singular: route - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ingress[0].host - name: Host - type: string - - jsonPath: .status.ingress[0].conditions[?(@.type=="Admitted")].status - name: Admitted - type: string - - jsonPath: .spec.to.name - name: Service - type: string - - jsonPath: .spec.tls.type - name: TLS - type: string - name: v1 - schema: - openAPIV3Schema: - description: "A route allows developers to expose services through an HTTP(S) - aware load balancing and proxy layer via a public DNS entry. The route may - further specify TLS options and a certificate, or specify a public CNAME - that the router should also accept for HTTP and HTTPS traffic. An administrator - typically configures their router to be visible outside the cluster firewall, - and may also add additional security, caching, or traffic controls on the - service content. Routers usually talk directly to the service endpoints. - \n Once a route is created, the `host` field may not be changed. Generally, - routers use the oldest route with a given host when resolving conflicts. - \n Routers are subject to additional customization and may support additional - controls via the annotations field. \n Because administrators may configure - multiple routers, the route status field is used to return information to - clients about the names and states of the route under each router. If a - client chooses a duplicate name, for instance, the route status conditions - are used to indicate the route cannot be chosen. \n To enable HTTP/2 ALPN - on a route it requires a custom (non-wildcard) certificate. This prevents - connection coalescing by clients, notably web browsers. We do not support - HTTP/2 ALPN on routes that use the default certificate because of the risk - of connection re-use/coalescing. Routes that do not have their own custom - certificate will not be HTTP/2 ALPN-enabled on either the frontend or the - backend. \n Compatibility level 1: Stable within a major release for a minimum - of 12 months or 3 minor releases (whichever is longer)." - 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 is the desired state of the route - properties: - alternateBackends: - description: alternateBackends allows up to 3 additional backends - to be assigned to the route. Only the Service kind is allowed, and - it will be defaulted to Service. Use the weight field in RouteTargetReference - object to specify relative preference. - items: - description: RouteTargetReference specifies the target that resolve - into endpoints. Only the 'Service' kind is allowed. Use 'weight' - field to emphasize one over others. - properties: - kind: - default: Service - description: The kind of target that the route is referring - to. Currently, only 'Service' is allowed - enum: - - Service - - "" - type: string - name: - description: name of the service/target that is being referred - to. e.g. name of the service - minLength: 1 - type: string - weight: - default: 100 - description: weight as an integer between 0 and 256, default - 100, that specifies the target's relative weight against other - target reference objects. 0 suppresses requests to this backend. - format: int32 - maximum: 256 - minimum: 0 - type: integer - required: - - kind - - name - type: object - maxItems: 3 - type: array - host: - description: host is an alias/DNS that points to the service. Optional. - If not specified a route name will typically be automatically chosen. - Must follow DNS952 subdomain conventions. - maxLength: 253 - pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ - type: string - httpHeaders: - description: httpHeaders defines policy for HTTP headers. - properties: - actions: - description: 'actions specifies options for modifying headers - and their values. Note that this option only applies to cleartext - HTTP connections and to secure HTTP connections for which the - ingress controller terminates encryption (that is, edge-terminated - or reencrypt connections). Headers cannot be modified for TLS - passthrough connections. Setting the HSTS (`Strict-Transport-Security`) - header is not supported via actions. `Strict-Transport-Security` - may only be configured using the "haproxy.router.openshift.io/hsts_header" - route annotation, and only in accordance with the policy specified - in Ingress.Spec.RequiredHSTSPolicies. In case of HTTP request - headers, the actions specified in spec.httpHeaders.actions on - the Route will be executed after the actions specified in the - IngressController''s spec.httpHeaders.actions field. In case - of HTTP response headers, the actions specified in spec.httpHeaders.actions - on the IngressController will be executed after the actions - specified in the Route''s spec.httpHeaders.actions field. The - headers set via this API will not appear in access logs. Any - actions defined here are applied after any actions related to - the following other fields: cache-control, spec.clientTLS, spec.httpHeaders.forwardedHeaderPolicy, - spec.httpHeaders.uniqueId, and spec.httpHeaders.headerNameCaseAdjustments. - The following header names are reserved and may not be modified - via this API: Strict-Transport-Security, Proxy, Cookie, Set-Cookie. - Note that the total size of all net added headers *after* interpolating - dynamic values must not exceed the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. Please refer to the documentation - for that API field for more details.' - properties: - request: - description: 'request is a list of HTTP request headers to - modify. Currently, actions may define to either `Set` or - `Delete` headers values. Actions defined here will modify - the request headers of all requests made through a route. - These actions are applied to a specific Route defined within - a cluster i.e. connections made through a route. Currently, - actions may define to either `Set` or `Delete` headers values. - Route actions will be executed after IngressController actions - for request headers. Actions are applied in sequence as - defined in this list. A maximum of 20 request header actions - may be configured. You can use this field to specify HTTP - request headers that should be set or deleted when forwarding - connections from the client to your application. Sample - fetchers allowed are "req.hdr" and "ssl_c_der". Converters - allowed are "lower" and "base64". Example header values: - "%[req.hdr(X-target),lower]", "%{+Q}[ssl_c_der,base64]". - Any request header configuration applied directly via a - Route resource using this API will override header configuration - for a header of the same name applied via spec.httpHeaders.actions - on the IngressController or route annotation. Note: This - field cannot be used if your route uses TLS passthrough.' - items: - description: RouteHTTPHeader specifies configuration for - setting or deleting an HTTP header. - properties: - action: - description: action specifies actions to perform on - headers, such as setting or deleting headers. - properties: - set: - description: 'set defines the HTTP header that should - be set: added if it doesn''t exist or replaced - if it does. This field is required when type is - Set and forbidden otherwise.' - properties: - value: - description: value specifies a header value. - Dynamic values can be added. The value will - be interpreted as an HAProxy format string - as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise - must be a valid HTTP header value as defined - in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - The value of this field must be no more than - 16384 characters in length. Note that the - total size of all net added headers *after* - interpolating dynamic values must not exceed - the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. - maxLength: 16384 - minLength: 1 - type: string - required: - - value - type: object - type: - description: type defines the type of the action - to be applied on the header. Possible values are - Set or Delete. Set allows you to set HTTP request - and response headers. Delete allows you to delete - HTTP request and response headers. - enum: - - Set - - Delete - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: set is required when type is Set, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Set'' ? has(self.set) - : !has(self.set)' - name: - description: 'name specifies the name of a header on - which to perform an action. Its value must be a valid - HTTP header name as defined in RFC 2616 section 4.2. - The name must consist only of alphanumeric and the - following special characters, "-!#$%&''*+.^_`". The - following header names are reserved and may not be - modified via this API: Strict-Transport-Security, - Proxy, Cookie, Set-Cookie. It must be no more than - 255 characters in length. Header name must be unique.' - maxLength: 255 - minLength: 1 - pattern: ^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$ - type: string - x-kubernetes-validations: - - message: strict-transport-security header may not - be modified via header actions - rule: self.lowerAscii() != 'strict-transport-security' - - message: proxy header may not be modified via header - actions - rule: self.lowerAscii() != 'proxy' - - message: cookie header may not be modified via header - actions - rule: self.lowerAscii() != 'cookie' - - message: set-cookie header may not be modified via - header actions - rule: self.lowerAscii() != 'set-cookie' - required: - - action - - name - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: Either the header value provided is not in correct - format or the sample fetcher/converter specified is not - allowed. The dynamic header value will be interpreted - as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise must be - a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - Sample fetchers allowed are req.hdr, ssl_c_der. Converters - allowed are lower, base64. - rule: self.all(key, key.action.type == "Delete" || (has(key.action.set) - && key.action.set.value.matches('^(?:%(?:%|(?:\\{[-+]?[QXE](?:,[-+]?[QXE])*\\})?\\[(?:req\\.hdr\\([0-9A-Za-z-]+\\)|ssl_c_der)(?:,(?:lower|base64))*\\])|[^%[:cntrl:]])+$'))) - response: - description: 'response is a list of HTTP response headers - to modify. Currently, actions may define to either `Set` - or `Delete` headers values. Actions defined here will modify - the response headers of all requests made through a route. - These actions are applied to a specific Route defined within - a cluster i.e. connections made through a route. Route actions - will be executed before IngressController actions for response - headers. Actions are applied in sequence as defined in this - list. A maximum of 20 response header actions may be configured. - You can use this field to specify HTTP response headers - that should be set or deleted when forwarding responses - from your application to the client. Sample fetchers allowed - are "res.hdr" and "ssl_c_der". Converters allowed are "lower" - and "base64". Example header values: "%[res.hdr(X-target),lower]", - "%{+Q}[ssl_c_der,base64]". Note: This field cannot be used - if your route uses TLS passthrough.' - items: - description: RouteHTTPHeader specifies configuration for - setting or deleting an HTTP header. - properties: - action: - description: action specifies actions to perform on - headers, such as setting or deleting headers. - properties: - set: - description: 'set defines the HTTP header that should - be set: added if it doesn''t exist or replaced - if it does. This field is required when type is - Set and forbidden otherwise.' - properties: - value: - description: value specifies a header value. - Dynamic values can be added. The value will - be interpreted as an HAProxy format string - as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise - must be a valid HTTP header value as defined - in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - The value of this field must be no more than - 16384 characters in length. Note that the - total size of all net added headers *after* - interpolating dynamic values must not exceed - the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. - maxLength: 16384 - minLength: 1 - type: string - required: - - value - type: object - type: - description: type defines the type of the action - to be applied on the header. Possible values are - Set or Delete. Set allows you to set HTTP request - and response headers. Delete allows you to delete - HTTP request and response headers. - enum: - - Set - - Delete - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: set is required when type is Set, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Set'' ? has(self.set) - : !has(self.set)' - name: - description: 'name specifies the name of a header on - which to perform an action. Its value must be a valid - HTTP header name as defined in RFC 2616 section 4.2. - The name must consist only of alphanumeric and the - following special characters, "-!#$%&''*+.^_`". The - following header names are reserved and may not be - modified via this API: Strict-Transport-Security, - Proxy, Cookie, Set-Cookie. It must be no more than - 255 characters in length. Header name must be unique.' - maxLength: 255 - minLength: 1 - pattern: ^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$ - type: string - x-kubernetes-validations: - - message: strict-transport-security header may not - be modified via header actions - rule: self.lowerAscii() != 'strict-transport-security' - - message: proxy header may not be modified via header - actions - rule: self.lowerAscii() != 'proxy' - - message: cookie header may not be modified via header - actions - rule: self.lowerAscii() != 'cookie' - - message: set-cookie header may not be modified via - header actions - rule: self.lowerAscii() != 'set-cookie' - required: - - action - - name - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: Either the header value provided is not in correct - format or the sample fetcher/converter specified is not - allowed. The dynamic header value will be interpreted - as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise must be - a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - Sample fetchers allowed are res.hdr, ssl_c_der. Converters - allowed are lower, base64. - rule: self.all(key, key.action.type == "Delete" || (has(key.action.set) - && key.action.set.value.matches('^(?:%(?:%|(?:\\{[-+]?[QXE](?:,[-+]?[QXE])*\\})?\\[(?:res\\.hdr\\([0-9A-Za-z-]+\\)|ssl_c_der)(?:,(?:lower|base64))*\\])|[^%[:cntrl:]])+$'))) - type: object - type: object - path: - description: path that the router watches for, to route traffic for - to the service. Optional - pattern: ^/ - type: string - port: - description: If specified, the port to be used by the router. Most - routers will use all endpoints exposed by the service by default - - set this value to instruct routers which port to use. - properties: - targetPort: - anyOf: - - type: integer - - type: string - description: The target port on pods selected by the service this - route points to. If this is a string, it will be looked up as - a named port in the target endpoints port list. Required - x-kubernetes-int-or-string: true - required: - - targetPort - type: object - subdomain: - description: "subdomain is a DNS subdomain that is requested within - the ingress controller's domain (as a subdomain). If host is set - this field is ignored. An ingress controller may choose to ignore - this suggested name, in which case the controller will report the - assigned name in the status.ingress array or refuse to admit the - route. If this value is set and the server does not support this - field host will be populated automatically. Otherwise host is left - empty. The field may have multiple parts separated by a dot, but - not all ingress controllers may honor the request. This field may - not be changed after creation except by a user with the update routes/custom-host - permission. \n Example: subdomain `frontend` automatically receives - the router subdomain `apps.mycluster.com` to have a full hostname - `frontend.apps.mycluster.com`." - maxLength: 253 - pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ - type: string - tls: - description: The tls field provides the ability to configure certificates - and termination for the route. - properties: - caCertificate: - description: caCertificate provides the cert authority certificate - contents - type: string - certificate: - description: certificate provides certificate contents. This should - be a single serving certificate, not a certificate chain. Do - not include a CA certificate. - type: string - destinationCACertificate: - description: destinationCACertificate provides the contents of - the ca certificate of the final destination. When using reencrypt - termination this file should be provided in order to have routers - use it for health checks on the secure connection. If this field - is not specified, the router may provide its own destination - CA and perform hostname validation using the short service name - (service.namespace.svc), which allows infrastructure generated - certificates to automatically verify. - type: string - externalCertificate: - description: externalCertificate provides certificate contents - as a secret reference. This should be a single serving certificate, - not a certificate chain. Do not include a CA certificate. The - secret referenced should be present in the same namespace as - that of the Route. Forbidden when `certificate` is set. - properties: - name: - description: 'name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - type: object - x-kubernetes-map-type: atomic - insecureEdgeTerminationPolicy: - description: "insecureEdgeTerminationPolicy indicates the desired - behavior for insecure connections to a route. While each router - may make its own decisions on which ports to expose, this is - normally port 80. \n * Allow - traffic is sent to the server - on the insecure port (edge/reencrypt terminations only) (default). - * None - no traffic is allowed on the insecure port. * Redirect - - clients are redirected to the secure port." - enum: - - Allow - - None - - Redirect - - "" - type: string - key: - description: key provides key file contents - type: string - termination: - description: "termination indicates termination type. \n * edge - - TLS termination is done by the router and http is used to - communicate with the backend (default) * passthrough - Traffic - is sent straight to the destination without the router providing - TLS termination * reencrypt - TLS termination is done by the - router and https is used to communicate with the backend \n - Note: passthrough termination is incompatible with httpHeader - actions" - enum: - - edge - - reencrypt - - passthrough - type: string - required: - - termination - type: object - x-kubernetes-validations: - - message: cannot have both spec.tls.certificate and spec.tls.externalCertificate - rule: '!(has(self.certificate) && has(self.externalCertificate))' - - message: 'cannot have both spec.tls.termination: passthrough and - spec.tls.insecureEdgeTerminationPolicy: Allow' - rule: 'has(self.termination) && has(self.insecureEdgeTerminationPolicy) - ? !((self.termination==''passthrough'') && (self.insecureEdgeTerminationPolicy==''Allow'')) - : true' - to: - description: to is an object the route should use as the primary backend. - Only the Service kind is allowed, and it will be defaulted to Service. - If the weight field (0-256 default 100) is set to zero, no traffic - will be sent to this backend. - properties: - kind: - default: Service - description: The kind of target that the route is referring to. - Currently, only 'Service' is allowed - enum: - - Service - - "" - type: string - name: - description: name of the service/target that is being referred - to. e.g. name of the service - minLength: 1 - type: string - weight: - default: 100 - description: weight as an integer between 0 and 256, default 100, - that specifies the target's relative weight against other target - reference objects. 0 suppresses requests to this backend. - format: int32 - maximum: 256 - minimum: 0 - type: integer - required: - - kind - - name - type: object - wildcardPolicy: - default: None - description: Wildcard policy if any for the route. Currently only - 'Subdomain' or 'None' is allowed. - enum: - - None - - Subdomain - - "" - type: string - required: - - to - type: object - x-kubernetes-validations: - - message: header actions are not permitted when tls termination is passthrough. - rule: '!has(self.tls) || self.tls.termination != ''passthrough'' || - !has(self.httpHeaders)' - status: - description: status is the current state of the route - properties: - ingress: - description: ingress describes the places where the route may be exposed. - The list of ingress points may contain duplicate Host or RouterName - values. Routes are considered live once they are `Ready` - items: - description: RouteIngress holds information about the places where - a route is exposed. - properties: - conditions: - description: Conditions is the state of the route, may be empty. - items: - description: RouteIngressCondition contains details for the - current condition of this route on a particular router. - properties: - lastTransitionTime: - description: RFC 3339 date and time when this condition - last transitioned - format: date-time - type: string - message: - description: Human readable message indicating details - about last transition. - type: string - reason: - description: (brief) reason for the condition's last transition, - and is usually a machine and human readable constant - type: string - status: - description: Status is the status of the condition. Can - be True, False, Unknown. - type: string - type: - description: Type is the type of the condition. Currently - only Admitted or UnservableInFutureVersions. - type: string - required: - - status - - type - type: object - type: array - host: - description: Host is the host string under which the route is - exposed; this value is required - type: string - routerCanonicalHostname: - description: CanonicalHostname is the external host name for - the router that can be used as a CNAME for the host requested - for this route. This value is optional and may not be set - in all cases. - type: string - routerName: - description: Name is a name chosen by the router to identify - itself; this value is required - type: string - wildcardPolicy: - description: Wildcard policy is the wildcard policy that was - allowed where this route is exposed. - type: string - type: object - type: array - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/vendor/github.com/openshift/api/route/v1/route.crd.yaml b/vendor/github.com/openshift/api/route/v1/route.crd.yaml deleted file mode 100644 index 0ec81826e..000000000 --- a/vendor/github.com/openshift/api/route/v1/route.crd.yaml +++ /dev/null @@ -1,655 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.openshift.io: https://github.com/openshift/api/pull/1228 - name: routes.route.openshift.io -spec: - group: route.openshift.io - names: - kind: Route - plural: routes - singular: route - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ingress[0].host - name: Host - type: string - - jsonPath: .status.ingress[0].conditions[?(@.type=="Admitted")].status - name: Admitted - type: string - - jsonPath: .spec.to.name - name: Service - type: string - - jsonPath: .spec.tls.type - name: TLS - type: string - name: v1 - schema: - openAPIV3Schema: - description: "A route allows developers to expose services through an HTTP(S) - aware load balancing and proxy layer via a public DNS entry. The route may - further specify TLS options and a certificate, or specify a public CNAME - that the router should also accept for HTTP and HTTPS traffic. An administrator - typically configures their router to be visible outside the cluster firewall, - and may also add additional security, caching, or traffic controls on the - service content. Routers usually talk directly to the service endpoints. - \n Once a route is created, the `host` field may not be changed. Generally, - routers use the oldest route with a given host when resolving conflicts. - \n Routers are subject to additional customization and may support additional - controls via the annotations field. \n Because administrators may configure - multiple routers, the route status field is used to return information to - clients about the names and states of the route under each router. If a - client chooses a duplicate name, for instance, the route status conditions - are used to indicate the route cannot be chosen. \n To enable HTTP/2 ALPN - on a route it requires a custom (non-wildcard) certificate. This prevents - connection coalescing by clients, notably web browsers. We do not support - HTTP/2 ALPN on routes that use the default certificate because of the risk - of connection re-use/coalescing. Routes that do not have their own custom - certificate will not be HTTP/2 ALPN-enabled on either the frontend or the - backend. \n Compatibility level 1: Stable within a major release for a minimum - of 12 months or 3 minor releases (whichever is longer)." - 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: - allOf: - - anyOf: - - properties: - path: - maxLength: 0 - - properties: - tls: - enum: - - null - - not: - properties: - tls: - properties: - termination: - enum: - - passthrough - - anyOf: - - not: - properties: - host: - maxLength: 0 - - not: - properties: - wildcardPolicy: - enum: - - Subdomain - description: spec is the desired state of the route - properties: - alternateBackends: - description: alternateBackends allows up to 3 additional backends - to be assigned to the route. Only the Service kind is allowed, and - it will be defaulted to Service. Use the weight field in RouteTargetReference - object to specify relative preference. - items: - description: RouteTargetReference specifies the target that resolve - into endpoints. Only the 'Service' kind is allowed. Use 'weight' - field to emphasize one over others. - properties: - kind: - default: Service - description: The kind of target that the route is referring - to. Currently, only 'Service' is allowed - enum: - - Service - - "" - type: string - name: - description: name of the service/target that is being referred - to. e.g. name of the service - minLength: 1 - type: string - weight: - default: 100 - description: weight as an integer between 0 and 256, default - 100, that specifies the target's relative weight against other - target reference objects. 0 suppresses requests to this backend. - format: int32 - maximum: 256 - minimum: 0 - type: integer - required: - - kind - - name - type: object - maxItems: 3 - type: array - host: - description: host is an alias/DNS that points to the service. Optional. - If not specified a route name will typically be automatically chosen. - Must follow DNS952 subdomain conventions. - maxLength: 253 - pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ - type: string - httpHeaders: - description: httpHeaders defines policy for HTTP headers. - properties: - actions: - description: 'actions specifies options for modifying headers - and their values. Note that this option only applies to cleartext - HTTP connections and to secure HTTP connections for which the - ingress controller terminates encryption (that is, edge-terminated - or reencrypt connections). Headers cannot be modified for TLS - passthrough connections. Setting the HSTS (`Strict-Transport-Security`) - header is not supported via actions. `Strict-Transport-Security` - may only be configured using the "haproxy.router.openshift.io/hsts_header" - route annotation, and only in accordance with the policy specified - in Ingress.Spec.RequiredHSTSPolicies. In case of HTTP request - headers, the actions specified in spec.httpHeaders.actions on - the Route will be executed after the actions specified in the - IngressController''s spec.httpHeaders.actions field. In case - of HTTP response headers, the actions specified in spec.httpHeaders.actions - on the IngressController will be executed after the actions - specified in the Route''s spec.httpHeaders.actions field. The - headers set via this API will not appear in access logs. Any - actions defined here are applied after any actions related to - the following other fields: cache-control, spec.clientTLS, spec.httpHeaders.forwardedHeaderPolicy, - spec.httpHeaders.uniqueId, and spec.httpHeaders.headerNameCaseAdjustments. - The following header names are reserved and may not be modified - via this API: Strict-Transport-Security, Proxy, Cookie, Set-Cookie. - Note that the total size of all net added headers *after* interpolating - dynamic values must not exceed the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. Please refer to the documentation - for that API field for more details.' - properties: - request: - description: 'request is a list of HTTP request headers to - modify. Currently, actions may define to either `Set` or - `Delete` headers values. Actions defined here will modify - the request headers of all requests made through a route. - These actions are applied to a specific Route defined within - a cluster i.e. connections made through a route. Currently, - actions may define to either `Set` or `Delete` headers values. - Route actions will be executed after IngressController actions - for request headers. Actions are applied in sequence as - defined in this list. A maximum of 20 request header actions - may be configured. You can use this field to specify HTTP - request headers that should be set or deleted when forwarding - connections from the client to your application. Sample - fetchers allowed are "req.hdr" and "ssl_c_der". Converters - allowed are "lower" and "base64". Example header values: - "%[req.hdr(X-target),lower]", "%{+Q}[ssl_c_der,base64]". - Any request header configuration applied directly via a - Route resource using this API will override header configuration - for a header of the same name applied via spec.httpHeaders.actions - on the IngressController or route annotation. Note: This - field cannot be used if your route uses TLS passthrough.' - items: - description: RouteHTTPHeader specifies configuration for - setting or deleting an HTTP header. - properties: - action: - description: action specifies actions to perform on - headers, such as setting or deleting headers. - properties: - set: - description: 'set defines the HTTP header that should - be set: added if it doesn''t exist or replaced - if it does. This field is required when type is - Set and forbidden otherwise.' - properties: - value: - description: value specifies a header value. - Dynamic values can be added. The value will - be interpreted as an HAProxy format string - as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise - must be a valid HTTP header value as defined - in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - The value of this field must be no more than - 16384 characters in length. Note that the - total size of all net added headers *after* - interpolating dynamic values must not exceed - the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. - maxLength: 16384 - minLength: 1 - type: string - required: - - value - type: object - type: - description: type defines the type of the action - to be applied on the header. Possible values are - Set or Delete. Set allows you to set HTTP request - and response headers. Delete allows you to delete - HTTP request and response headers. - enum: - - Set - - Delete - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: set is required when type is Set, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Set'' ? has(self.set) - : !has(self.set)' - name: - description: 'name specifies the name of a header on - which to perform an action. Its value must be a valid - HTTP header name as defined in RFC 2616 section 4.2. - The name must consist only of alphanumeric and the - following special characters, "-!#$%&''*+.^_`". The - following header names are reserved and may not be - modified via this API: Strict-Transport-Security, - Proxy, Cookie, Set-Cookie. It must be no more than - 255 characters in length. Header name must be unique.' - maxLength: 255 - minLength: 1 - pattern: ^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$ - type: string - x-kubernetes-validations: - - message: strict-transport-security header may not - be modified via header actions - rule: self.lowerAscii() != 'strict-transport-security' - - message: proxy header may not be modified via header - actions - rule: self.lowerAscii() != 'proxy' - - message: cookie header may not be modified via header - actions - rule: self.lowerAscii() != 'cookie' - - message: set-cookie header may not be modified via - header actions - rule: self.lowerAscii() != 'set-cookie' - required: - - action - - name - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: Either the header value provided is not in correct - format or the sample fetcher/converter specified is not - allowed. The dynamic header value will be interpreted - as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise must be - a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - Sample fetchers allowed are req.hdr, ssl_c_der. Converters - allowed are lower, base64. - rule: self.all(key, key.action.type == "Delete" || (has(key.action.set) - && key.action.set.value.matches('^(?:%(?:%|(?:\\{[-+]?[QXE](?:,[-+]?[QXE])*\\})?\\[(?:req\\.hdr\\([0-9A-Za-z-]+\\)|ssl_c_der)(?:,(?:lower|base64))*\\])|[^%[:cntrl:]])+$'))) - response: - description: 'response is a list of HTTP response headers - to modify. Currently, actions may define to either `Set` - or `Delete` headers values. Actions defined here will modify - the response headers of all requests made through a route. - These actions are applied to a specific Route defined within - a cluster i.e. connections made through a route. Route actions - will be executed before IngressController actions for response - headers. Actions are applied in sequence as defined in this - list. A maximum of 20 response header actions may be configured. - You can use this field to specify HTTP response headers - that should be set or deleted when forwarding responses - from your application to the client. Sample fetchers allowed - are "res.hdr" and "ssl_c_der". Converters allowed are "lower" - and "base64". Example header values: "%[res.hdr(X-target),lower]", - "%{+Q}[ssl_c_der,base64]". Note: This field cannot be used - if your route uses TLS passthrough.' - items: - description: RouteHTTPHeader specifies configuration for - setting or deleting an HTTP header. - properties: - action: - description: action specifies actions to perform on - headers, such as setting or deleting headers. - properties: - set: - description: 'set defines the HTTP header that should - be set: added if it doesn''t exist or replaced - if it does. This field is required when type is - Set and forbidden otherwise.' - properties: - value: - description: value specifies a header value. - Dynamic values can be added. The value will - be interpreted as an HAProxy format string - as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise - must be a valid HTTP header value as defined - in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - The value of this field must be no more than - 16384 characters in length. Note that the - total size of all net added headers *after* - interpolating dynamic values must not exceed - the value of spec.tuningOptions.headerBufferMaxRewriteBytes - on the IngressController. - maxLength: 16384 - minLength: 1 - type: string - required: - - value - type: object - type: - description: type defines the type of the action - to be applied on the header. Possible values are - Set or Delete. Set allows you to set HTTP request - and response headers. Delete allows you to delete - HTTP request and response headers. - enum: - - Set - - Delete - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: set is required when type is Set, and forbidden - otherwise - rule: 'has(self.type) && self.type == ''Set'' ? has(self.set) - : !has(self.set)' - name: - description: 'name specifies the name of a header on - which to perform an action. Its value must be a valid - HTTP header name as defined in RFC 2616 section 4.2. - The name must consist only of alphanumeric and the - following special characters, "-!#$%&''*+.^_`". The - following header names are reserved and may not be - modified via this API: Strict-Transport-Security, - Proxy, Cookie, Set-Cookie. It must be no more than - 255 characters in length. Header name must be unique.' - maxLength: 255 - minLength: 1 - pattern: ^[-!#$%&'*+.0-9A-Z^_`a-z|~]+$ - type: string - x-kubernetes-validations: - - message: strict-transport-security header may not - be modified via header actions - rule: self.lowerAscii() != 'strict-transport-security' - - message: proxy header may not be modified via header - actions - rule: self.lowerAscii() != 'proxy' - - message: cookie header may not be modified via header - actions - rule: self.lowerAscii() != 'cookie' - - message: set-cookie header may not be modified via - header actions - rule: self.lowerAscii() != 'set-cookie' - required: - - action - - name - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: Either the header value provided is not in correct - format or the sample fetcher/converter specified is not - allowed. The dynamic header value will be interpreted - as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 - and may use HAProxy's %[] syntax and otherwise must be - a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. - Sample fetchers allowed are res.hdr, ssl_c_der. Converters - allowed are lower, base64. - rule: self.all(key, key.action.type == "Delete" || (has(key.action.set) - && key.action.set.value.matches('^(?:%(?:%|(?:\\{[-+]?[QXE](?:,[-+]?[QXE])*\\})?\\[(?:res\\.hdr\\([0-9A-Za-z-]+\\)|ssl_c_der)(?:,(?:lower|base64))*\\])|[^%[:cntrl:]])+$'))) - type: object - type: object - path: - description: path that the router watches for, to route traffic for - to the service. Optional - pattern: ^/ - type: string - port: - description: If specified, the port to be used by the router. Most - routers will use all endpoints exposed by the service by default - - set this value to instruct routers which port to use. - properties: - targetPort: - allOf: - - not: - enum: - - 0 - - not: - enum: - - "" - x-kubernetes-int-or-string: true - required: - - targetPort - type: object - subdomain: - description: "subdomain is a DNS subdomain that is requested within - the ingress controller's domain (as a subdomain). If host is set - this field is ignored. An ingress controller may choose to ignore - this suggested name, in which case the controller will report the - assigned name in the status.ingress array or refuse to admit the - route. If this value is set and the server does not support this - field host will be populated automatically. Otherwise host is left - empty. The field may have multiple parts separated by a dot, but - not all ingress controllers may honor the request. This field may - not be changed after creation except by a user with the update routes/custom-host - permission. \n Example: subdomain `frontend` automatically receives - the router subdomain `apps.mycluster.com` to have a full hostname - `frontend.apps.mycluster.com`." - maxLength: 253 - pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ - type: string - tls: - allOf: - - anyOf: - - properties: - caCertificate: - maxLength: 0 - certificate: - maxLength: 0 - destinationCACertificate: - maxLength: 0 - key: - maxLength: 0 - - not: - properties: - termination: - enum: - - passthrough - - anyOf: - - properties: - destinationCACertificate: - maxLength: 0 - - not: - properties: - termination: - enum: - - edge - description: The tls field provides the ability to configure certificates - and termination for the route. - properties: - caCertificate: - description: caCertificate provides the cert authority certificate - contents - type: string - certificate: - description: certificate provides certificate contents. This should - be a single serving certificate, not a certificate chain. Do - not include a CA certificate. - type: string - destinationCACertificate: - description: destinationCACertificate provides the contents of - the ca certificate of the final destination. When using reencrypt - termination this file should be provided in order to have routers - use it for health checks on the secure connection. If this field - is not specified, the router may provide its own destination - CA and perform hostname validation using the short service name - (service.namespace.svc), which allows infrastructure generated - certificates to automatically verify. - type: string - insecureEdgeTerminationPolicy: - description: "insecureEdgeTerminationPolicy indicates the desired - behavior for insecure connections to a route. While each router - may make its own decisions on which ports to expose, this is - normally port 80. \n * Allow - traffic is sent to the server - on the insecure port (edge/reencrypt terminations only) (default). - * None - no traffic is allowed on the insecure port. * Redirect - - clients are redirected to the secure port." - enum: - - Allow - - None - - Redirect - - "" - type: string - key: - description: key provides key file contents - type: string - termination: - description: "termination indicates termination type. \n * edge - - TLS termination is done by the router and http is used to - communicate with the backend (default) * passthrough - Traffic - is sent straight to the destination without the router providing - TLS termination * reencrypt - TLS termination is done by the - router and https is used to communicate with the backend \n - Note: passthrough termination is incompatible with httpHeader - actions" - enum: - - edge - - reencrypt - - passthrough - type: string - required: - - termination - type: object - x-kubernetes-validations: - - message: 'cannot have both spec.tls.termination: passthrough and - spec.tls.insecureEdgeTerminationPolicy: Allow' - rule: 'has(self.termination) && has(self.insecureEdgeTerminationPolicy) - ? !((self.termination==''passthrough'') && (self.insecureEdgeTerminationPolicy==''Allow'')) - : true' - to: - description: to is an object the route should use as the primary backend. - Only the Service kind is allowed, and it will be defaulted to Service. - If the weight field (0-256 default 100) is set to zero, no traffic - will be sent to this backend. - properties: - kind: - default: Service - description: The kind of target that the route is referring to. - Currently, only 'Service' is allowed - enum: - - Service - - "" - type: string - name: - description: name of the service/target that is being referred - to. e.g. name of the service - minLength: 1 - type: string - weight: - default: 100 - description: weight as an integer between 0 and 256, default 100, - that specifies the target's relative weight against other target - reference objects. 0 suppresses requests to this backend. - format: int32 - maximum: 256 - minimum: 0 - type: integer - required: - - kind - - name - type: object - wildcardPolicy: - default: None - description: Wildcard policy if any for the route. Currently only - 'Subdomain' or 'None' is allowed. - enum: - - None - - Subdomain - - "" - type: string - required: - - to - type: object - x-kubernetes-validations: - - message: header actions are not permitted when tls termination is passthrough. - rule: '!has(self.tls) || self.tls.termination != ''passthrough'' || - !has(self.httpHeaders)' - status: - description: status is the current state of the route - properties: - ingress: - description: ingress describes the places where the route may be exposed. - The list of ingress points may contain duplicate Host or RouterName - values. Routes are considered live once they are `Ready` - items: - description: RouteIngress holds information about the places where - a route is exposed. - properties: - conditions: - description: Conditions is the state of the route, may be empty. - items: - description: RouteIngressCondition contains details for the - current condition of this route on a particular router. - properties: - lastTransitionTime: - description: RFC 3339 date and time when this condition - last transitioned - format: date-time - type: string - message: - description: Human readable message indicating details - about last transition. - type: string - reason: - description: (brief) reason for the condition's last transition, - and is usually a machine and human readable constant - type: string - status: - description: Status is the status of the condition. Can - be True, False, Unknown. - type: string - type: - description: Type is the type of the condition. Currently - only Admitted or UnservableInFutureVersions. - type: string - required: - - status - - type - type: object - type: array - host: - description: Host is the host string under which the route is - exposed; this value is required - type: string - routerCanonicalHostname: - description: CanonicalHostname is the external host name for - the router that can be used as a CNAME for the host requested - for this route. This value is optional and may not be set - in all cases. - type: string - routerName: - description: Name is a name chosen by the router to identify - itself; this value is required - type: string - wildcardPolicy: - description: Wildcard policy is the wildcard policy that was - allowed where this route is exposed. - type: string - type: object - type: array - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/vendor/github.com/openshift/api/route/v1/route.crd.yaml-patch b/vendor/github.com/openshift/api/route/v1/route.crd.yaml-patch deleted file mode 100644 index 7f09302f3..000000000 --- a/vendor/github.com/openshift/api/route/v1/route.crd.yaml-patch +++ /dev/null @@ -1,67 +0,0 @@ -- op: add - path: /spec/versions/name=v1/schema/openAPIV3Schema/properties/spec/allOf - value: - # spec.path must be empty when using passthrough TLS. - - anyOf: - - properties: - path: - maxLength: 0 - - properties: - tls: - enum: [null] - - not: - properties: - tls: - properties: - termination: - enum: ["passthrough"] - # spec.host must be nonempty for a wildcard route. - - anyOf: - - not: - properties: - host: - maxLength: 0 - - not: - properties: - wildcardPolicy: - enum: ["Subdomain"] -- op: add - path: /spec/versions/name=v1/schema/openAPIV3Schema/properties/spec/properties/port/properties/targetPort - value: - # spec.port.targetPort cannot be the integer 0 or the empty string. (Note - # that negative integer values are allowed, as is the string "0".) - allOf: - - not: - enum: [0] - - not: - enum: [""] - x-kubernetes-int-or-string: true -- op: add - path: /spec/versions/name=v1/schema/openAPIV3Schema/properties/spec/properties/tls/allOf - value: - # spec.tls.certificate, spec.tls.key, spec.tls.caCertificate, and - # spec.tls.destinationCACertificate must omitted when using passthrough TLS. - - anyOf: - - properties: - certificate: - maxLength: 0 - key: - maxLength: 0 - caCertificate: - maxLength: 0 - destinationCACertificate: - maxLength: 0 - - not: - properties: - termination: - enum: ["passthrough"] - # spec.tls.destinationCACertificate must be omitted when using edge-terminated - # TLS. - - anyOf: - - properties: - destinationCACertificate: - maxLength: 0 - - not: - properties: - termination: - enum: ["edge"] diff --git a/vendor/github.com/openshift/api/route/v1/stable.route.testsuite.yaml b/vendor/github.com/openshift/api/route/v1/stable.route.testsuite.yaml deleted file mode 100644 index d1e476673..000000000 --- a/vendor/github.com/openshift/api/route/v1/stable.route.testsuite.yaml +++ /dev/null @@ -1,675 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this -name: "[Stable] Route" -crd: route.crd.yaml -tests: - onCreate: - - name: Should be able to create a minimal Route - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - weight: 100 - wildcardPolicy: None - - name: "cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow" - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Allow - expectedError: "cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow" - - name: "spec.tls.termination: passthrough is compatible with spec.tls.insecureEdgeTerminationPolicy: Redirect" - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Redirect - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - weight: 100 - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Redirect - wildcardPolicy: None - - name: "spec.tls.termination: passthrough is compatible with spec.tls.insecureEdgeTerminationPolicy: None" - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: None - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - weight: 100 - tls: - termination: passthrough - insecureEdgeTerminationPolicy: None - wildcardPolicy: None - - name: Should be able to create a Route with valid actions - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-actions - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Frame-Options - action: - type: Set - set: - value: DENY - - name: X-Cache-Info - action: - type: Set - set: - value: "not cacheable; meta data too large" - - name: X-XSS-Protection - action: - type: Delete - - name: X-Source - action: - type: Set - set: - value: "%[res.hdr(X-Value),lower]" - request: - - name: Content-Location - action: - type: Set - set: - value: /my-first-blog-post - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: "%{+Q}[ssl_c_der,base64]" - - name: Content-Language - action: - type: Delete - - name: X-Target - action: - type: Set - set: - value: "%[req.hdr(host),lower]" - - name: X-Conditional - action: - type: Set - set: - value: "%[req.hdr(Host)] if foo" - - name: X-Condition - action: - type: Set - set: - value: "%[req.hdr(Host)]\ if\ foo" - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-actions - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - weight: 100 - wildcardPolicy: None - httpHeaders: - actions: - response: - - name: X-Frame-Options - action: - type: Set - set: - value: DENY - - name: X-Cache-Info - action: - type: Set - set: - value: "not cacheable; meta data too large" - - name: X-XSS-Protection - action: - type: Delete - - name: X-Source - action: - type: Set - set: - value: "%[res.hdr(X-Value),lower]" - request: - - name: Content-Location - action: - type: Set - set: - value: /my-first-blog-post - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: "%{+Q}[ssl_c_der,base64]" - - name: Content-Language - action: - type: Delete - - name: X-Target - action: - type: Set - set: - value: "%[req.hdr(host),lower]" - - name: X-Conditional - action: - type: Set - set: - value: "%[req.hdr(Host)] if foo" - - name: X-Condition - action: - type: Set - set: - value: "%[req.hdr(Host)]\ if\ foo" - - name: "Should not allow response header actions if tls termination is set to passthrough" - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-passthrough - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: passthrough - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Frame-Options - action: - type: Set - set: - value: DENY - - name: X-XSS-Protection - action: - type: Delete - expectedError: "header actions are not permitted when tls termination is passthrough." - - name: "Should not allow request header actions if tls termination is set to passthrough" - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-passthrough - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: passthrough - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: Content-Location - action: - type: Set - set: - value: /my-first-blog-post - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: "%{+Q}[ssl_c_der,base64]" - - name: Content-Language - action: - type: Delete - - name: X-Target - action: - type: Set - set: - value: "%[req.hdr(host),lower]" - expectedError: "header actions are not permitted when tls termination is passthrough." - - name: Should not allow to set/delete HSTS header. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-hsts - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Frame-Options - action: - type: Set - set: - value: DENY - - name: Strict-Transport-Security - action: - type: Delete - request: - - name: Content-Location - action: - type: Set - set: - value: /my-first-blog-post - - name: Content-Language - action: - type: Delete - expectedError: "strict-transport-security header may not be modified via header actions" - - name: Should not allow to set proxy request header. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - name: hello-openshift-edge-proxy - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: Proxy - action: - type: Set - set: - value: example.xyz - expectedError: "proxy header may not be modified via header actions" - - name: Should not allow to set cookie header. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - name: hello-openshift-edge-proxy - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: Cookie - action: - type: Set - set: - value: "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1" - expectedError: "cookie header may not be modified via header actions" - - name: Should not allow to set set-cookie header. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - name: hello-openshift-edge-proxy - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: Set-Cookie - action: - type: Set - set: - value: "sessionId=e8bb43229de9; Domain=foo.example.com" - expectedError: "set-cookie header may not be modified via header actions" - - name: Should not allow to set/delete dynamic headers with unclosed braces. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-unclosed-braces - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: Content-Location - action: - type: Set - set: - value: /my-first-blog-post - - name: Content-Language - action: - type: Delete - - name: expires - action: - type: Set - set: - value: "%[req.hdr(host),lower" - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are req.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow to set dynamic response header values with not allowed sample fetchers. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Target - action: - type: Set - set: - value: "%{+Q}[ssl_c_der1,base64]" - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are res.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow to set/delete dynamic response header values with not allowed converters. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Target - action: - type: Set - set: - value: "%{+Q}[ssl_c_der,bogus]" - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are res.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow to set/delete dynamic response header values containing req.hdr fetcher. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Target - action: - type: Set - set: - value: "%[req.hdr(host),lower]" - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are res.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow to set/delete dynamic response header values containing req.hdr fetcher. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: X-Source - action: - type: Set - set: - value: "%[res.hdr(X-Value),lower]" - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are req.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow to set/delete dynamic request header values with not allowed converters. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: "%{+Q}[ssl_c_der,bogus]" - - name: Content-Language - action: - type: Delete - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are req.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow to set dynamic request header values with not allowed sample fetchers. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: "%{+Q}[ssl_c_der1122,base64]" - - name: Content-Language - action: - type: Delete - expectedError: "Either the header value provided is not in correct format or the sample fetcher/converter specified is not allowed. The dynamic header value will be interpreted as an HAProxy format string as defined in http://cbonte.github.io/haproxy-dconv/2.6/configuration.html#8.2.6 and may use HAProxy's %[] syntax and otherwise must be a valid HTTP header value as defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2. Sample fetchers allowed are req.hdr, ssl_c_der. Converters allowed are lower, base64." - - name: Should not allow empty value in request - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - request: - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: - expectedError: 'Route.route.openshift.io "hello-openshift-edge-not-allowed-values" is invalid: [spec.httpHeaders.actions.request[0].action.set.value: Required value, : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation]' - - name: Should not allow empty value in response - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-edge-not-allowed-values - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-SSL-Client-Cert - action: - type: Set - set: - value: - expectedError: 'Route.route.openshift.io "hello-openshift-edge-not-allowed-values" is invalid: [spec.httpHeaders.actions.response[0].action.set.value: Required value, : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation]' - - name: Should be required to specify the set field when the discriminant type is Set. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-actions - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Frame-Options - action: - type: Set - expectedError: "set is required when type is Set, and forbidden otherwise" - - name: Should be required to specify the set field when the discriminant type is Set. - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - type: sharded - name: hello-openshift-actions - namespace: hello-openshift - spec: - subdomain: hello-openshift - tls: - termination: edge - to: - kind: Service - name: hello-openshift - httpHeaders: - actions: - response: - - name: X-Frame-Options - action: - set: - value: DENY - expectedError: 'Route.route.openshift.io "hello-openshift-actions" is invalid: [spec.httpHeaders.actions.response[0].action.type: Required value, : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation]' diff --git a/vendor/github.com/openshift/api/route/v1/techpreview.route.testsuite.yaml b/vendor/github.com/openshift/api/route/v1/techpreview.route.testsuite.yaml deleted file mode 100644 index 0f0cdd11b..000000000 --- a/vendor/github.com/openshift/api/route/v1/techpreview.route.testsuite.yaml +++ /dev/null @@ -1,103 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this -name: '[TechPreview] Route' -crd: route-TechPreviewNoUpgrade.crd.yaml -tests: - onCreate: - - name: Should be able to create a minimal Route - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - weight: 100 - wildcardPolicy: None - - name: 'cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Allow - expectedError: 'cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow' - - name: 'spec.tls.termination: passthrough is compatible with spec.tls.insecureEdgeTerminationPolicy: Redirect' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Redirect - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - weight: 100 - tls: - termination: passthrough - insecureEdgeTerminationPolicy: Redirect - wildcardPolicy: None - - name: 'spec.tls.termination: passthrough is compatible with spec.tls.insecureEdgeTerminationPolicy: None' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - tls: - termination: passthrough - insecureEdgeTerminationPolicy: None - expected: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - host: test.foo - to: - kind: Service - name: foo - weight: 100 - tls: - termination: passthrough - insecureEdgeTerminationPolicy: None - wildcardPolicy: None - - name: 'cannot have both spec.tls.certificate and spec.tls.externalCertificate' - initial: | - apiVersion: route.openshift.io/v1 - kind: Route - spec: - to: - kind: Service - name: foo - tls: - termination: edge - key: |- - -----BEGIN RSA PRIVATE KEY----- - -----END RSA PRIVATE KEY----- - certificate: |- - -----BEGIN CERTIFICATE----- - -----END CERTIFICATE----- - externalCertificate: - name: "my-local-secret" - expectedError: 'Invalid value: "object": cannot have both spec.tls.certificate and spec.tls.externalCertificate' diff --git a/vendor/github.com/openshift/api/route/v1/types.go b/vendor/github.com/openshift/api/route/v1/types.go index b5a567d6a..cd5e5eced 100644 --- a/vendor/github.com/openshift/api/route/v1/types.go +++ b/vendor/github.com/openshift/api/route/v1/types.go @@ -10,6 +10,12 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:resource:path=routes,scope=Namespaced +// +openshift:api-approved.openshift.io=https://github.com/openshift/api/pull/1228 +// +kubebuilder:printcolumn:name=Host,JSONPath=.status.ingress[0].host,type=string +// +kubebuilder:printcolumn:name=Admitted,JSONPath=.status.ingress[0].conditions[?(@.type=="Admitted")].status,type=string +// +kubebuilder:printcolumn:name=Service,JSONPath=.spec.to.name,type=string +// +kubebuilder:printcolumn:name=TLS,JSONPath=.spec.tls.type,type=string // A route allows developers to expose services through an HTTP(S) aware load balancing and proxy // layer via a public DNS entry. The route may further specify TLS options and a certificate, or @@ -409,7 +415,7 @@ type RouterShard struct { // TLSConfig defines config used to secure a route and provide termination // // +kubebuilder:validation:XValidation:rule="has(self.termination) && has(self.insecureEdgeTerminationPolicy) ? !((self.termination=='passthrough') && (self.insecureEdgeTerminationPolicy=='Allow')) : true", message="cannot have both spec.tls.termination: passthrough and spec.tls.insecureEdgeTerminationPolicy: Allow" -// +openshift:validation:FeatureSetAwareXValidation:featureSet=TechPreviewNoUpgrade;CustomNoUpgrade,rule="!(has(self.certificate) && has(self.externalCertificate))", message="cannot have both spec.tls.certificate and spec.tls.externalCertificate" +// +openshift:validation:FeatureGateAwareXValidation:featureGate=ExternalRouteCertificate,rule="!(has(self.certificate) && has(self.externalCertificate))", message="cannot have both spec.tls.certificate and spec.tls.externalCertificate" type TLSConfig struct { // termination indicates termination type. // @@ -454,7 +460,7 @@ type TLSConfig struct { // be present in the same namespace as that of the Route. // Forbidden when `certificate` is set. // - // +openshift:enable:FeatureSets=CustomNoUpgrade;TechPreviewNoUpgrade + // +openshift:enable:FeatureGate=ExternalRouteCertificate // +optional ExternalCertificate *LocalObjectReference `json:"externalCertificate,omitempty" protobuf:"bytes,7,opt,name=externalCertificate"` } diff --git a/vendor/github.com/openshift/api/route/v1/zz_generated.featuregated-crd-manifests.yaml b/vendor/github.com/openshift/api/route/v1/zz_generated.featuregated-crd-manifests.yaml new file mode 100644 index 000000000..aced0855f --- /dev/null +++ b/vendor/github.com/openshift/api/route/v1/zz_generated.featuregated-crd-manifests.yaml @@ -0,0 +1,34 @@ +routes.route.openshift.io: + Annotations: {} + ApprovedPRNumber: https://github.com/openshift/api/pull/1228 + CRDName: routes.route.openshift.io + Capability: "" + Category: "" + FeatureGates: + - ExternalRouteCertificate + FilenameOperatorName: "" + FilenameOperatorOrdering: "" + FilenameRunLevel: "" + GroupName: route.openshift.io + HasStatus: true + KindName: Route + Labels: {} + PluralName: routes + PrinterColumns: + - jsonPath: .status.ingress[0].host + name: Host + type: string + - jsonPath: .status.ingress[0].conditions[?(@.type=="Admitted")].status + name: Admitted + type: string + - jsonPath: .spec.to.name + name: Service + type: string + - jsonPath: .spec.tls.type + name: TLS + type: string + Scope: Namespaced + ShortNames: null + TopLevelFeatureGates: [] + Version: v1 + diff --git a/vendor/github.com/openshift/client-go/project/clientset/versioned/clientset.go b/vendor/github.com/openshift/client-go/project/clientset/versioned/clientset.go index 7ca945a3f..3489558fd 100644 --- a/vendor/github.com/openshift/client-go/project/clientset/versioned/clientset.go +++ b/vendor/github.com/openshift/client-go/project/clientset/versioned/clientset.go @@ -17,8 +17,7 @@ type Interface interface { ProjectV1() projectv1.ProjectV1Interface } -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. +// Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient projectV1 *projectv1.ProjectV1Client diff --git a/vendor/github.com/openshift/client-go/project/clientset/versioned/doc.go b/vendor/github.com/openshift/client-go/project/clientset/versioned/doc.go deleted file mode 100644 index 0e0c2a890..000000000 --- a/vendor/github.com/openshift/client-go/project/clientset/versioned/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/vendor/github.com/openshift/client-go/project/clientset/versioned/fake/register.go b/vendor/github.com/openshift/client-go/project/clientset/versioned/fake/register.go index 55d568189..64269eabc 100644 --- a/vendor/github.com/openshift/client-go/project/clientset/versioned/fake/register.go +++ b/vendor/github.com/openshift/client-go/project/clientset/versioned/fake/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/vendor/github.com/openshift/client-go/project/clientset/versioned/scheme/register.go b/vendor/github.com/openshift/client-go/project/clientset/versioned/scheme/register.go index 5706a6c0c..062621ee1 100644 --- a/vendor/github.com/openshift/client-go/project/clientset/versioned/scheme/register.go +++ b/vendor/github.com/openshift/client-go/project/clientset/versioned/scheme/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_project.go b/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_project.go index 5840a63fc..88d3195d8 100644 --- a/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_project.go +++ b/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_project.go @@ -7,11 +7,10 @@ import ( json "encoding/json" "fmt" - projectv1 "github.com/openshift/api/project/v1" - applyconfigurationsprojectv1 "github.com/openshift/client-go/project/applyconfigurations/project/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "github.com/openshift/api/project/v1" + projectv1 "github.com/openshift/client-go/project/applyconfigurations/project/v1" + metav1 "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" @@ -22,24 +21,24 @@ type FakeProjects struct { Fake *FakeProjectV1 } -var projectsResource = schema.GroupVersionResource{Group: "project.openshift.io", Version: "v1", Resource: "projects"} +var projectsResource = v1.SchemeGroupVersion.WithResource("projects") -var projectsKind = schema.GroupVersionKind{Group: "project.openshift.io", Version: "v1", Kind: "Project"} +var projectsKind = v1.SchemeGroupVersion.WithKind("Project") // Get takes name of the project, and returns the corresponding project object, and an error if there is any. -func (c *FakeProjects) Get(ctx context.Context, name string, options v1.GetOptions) (result *projectv1.Project, err error) { +func (c *FakeProjects) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Project, err error) { obj, err := c.Fake. - Invokes(testing.NewRootGetAction(projectsResource, name), &projectv1.Project{}) + Invokes(testing.NewRootGetAction(projectsResource, name), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } // List takes label and field selectors, and returns the list of Projects that match those selectors. -func (c *FakeProjects) List(ctx context.Context, opts v1.ListOptions) (result *projectv1.ProjectList, err error) { +func (c *FakeProjects) List(ctx context.Context, opts metav1.ListOptions) (result *v1.ProjectList, err error) { obj, err := c.Fake. - Invokes(testing.NewRootListAction(projectsResource, projectsKind, opts), &projectv1.ProjectList{}) + Invokes(testing.NewRootListAction(projectsResource, projectsKind, opts), &v1.ProjectList{}) if obj == nil { return nil, err } @@ -48,8 +47,8 @@ func (c *FakeProjects) List(ctx context.Context, opts v1.ListOptions) (result *p if label == nil { label = labels.Everything() } - list := &projectv1.ProjectList{ListMeta: obj.(*projectv1.ProjectList).ListMeta} - for _, item := range obj.(*projectv1.ProjectList).Items { + list := &v1.ProjectList{ListMeta: obj.(*v1.ProjectList).ListMeta} + for _, item := range obj.(*v1.ProjectList).Items { if label.Matches(labels.Set(item.Labels)) { list.Items = append(list.Items, item) } @@ -58,69 +57,69 @@ func (c *FakeProjects) List(ctx context.Context, opts v1.ListOptions) (result *p } // Watch returns a watch.Interface that watches the requested projects. -func (c *FakeProjects) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { +func (c *FakeProjects) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewRootWatchAction(projectsResource, opts)) } // Create takes the representation of a project and creates it. Returns the server's representation of the project, and an error, if there is any. -func (c *FakeProjects) Create(ctx context.Context, project *projectv1.Project, opts v1.CreateOptions) (result *projectv1.Project, err error) { +func (c *FakeProjects) Create(ctx context.Context, project *v1.Project, opts metav1.CreateOptions) (result *v1.Project, err error) { obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(projectsResource, project), &projectv1.Project{}) + Invokes(testing.NewRootCreateAction(projectsResource, project), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } // Update takes the representation of a project and updates it. Returns the server's representation of the project, and an error, if there is any. -func (c *FakeProjects) Update(ctx context.Context, project *projectv1.Project, opts v1.UpdateOptions) (result *projectv1.Project, err error) { +func (c *FakeProjects) Update(ctx context.Context, project *v1.Project, opts metav1.UpdateOptions) (result *v1.Project, err error) { obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(projectsResource, project), &projectv1.Project{}) + Invokes(testing.NewRootUpdateAction(projectsResource, project), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), 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 *FakeProjects) UpdateStatus(ctx context.Context, project *projectv1.Project, opts v1.UpdateOptions) (*projectv1.Project, error) { +func (c *FakeProjects) UpdateStatus(ctx context.Context, project *v1.Project, opts metav1.UpdateOptions) (*v1.Project, error) { obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(projectsResource, "status", project), &projectv1.Project{}) + Invokes(testing.NewRootUpdateSubresourceAction(projectsResource, "status", project), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } // Delete takes name of the project and deletes it. Returns an error if one occurs. -func (c *FakeProjects) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { +func (c *FakeProjects) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { _, err := c.Fake. - Invokes(testing.NewRootDeleteActionWithOptions(projectsResource, name, opts), &projectv1.Project{}) + Invokes(testing.NewRootDeleteActionWithOptions(projectsResource, name, opts), &v1.Project{}) return err } // DeleteCollection deletes a collection of objects. -func (c *FakeProjects) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { +func (c *FakeProjects) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { action := testing.NewRootDeleteCollectionAction(projectsResource, listOpts) - _, err := c.Fake.Invokes(action, &projectv1.ProjectList{}) + _, err := c.Fake.Invokes(action, &v1.ProjectList{}) return err } // Patch applies the patch and returns the patched project. -func (c *FakeProjects) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *projectv1.Project, err error) { +func (c *FakeProjects) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Project, err error) { obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(projectsResource, name, pt, data, subresources...), &projectv1.Project{}) + Invokes(testing.NewRootPatchSubresourceAction(projectsResource, name, pt, data, subresources...), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } // Apply takes the given apply declarative configuration, applies it and returns the applied project. -func (c *FakeProjects) Apply(ctx context.Context, project *applyconfigurationsprojectv1.ProjectApplyConfiguration, opts v1.ApplyOptions) (result *projectv1.Project, err error) { +func (c *FakeProjects) Apply(ctx context.Context, project *projectv1.ProjectApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Project, err error) { if project == nil { return nil, fmt.Errorf("project provided to Apply must not be nil") } @@ -133,16 +132,16 @@ func (c *FakeProjects) Apply(ctx context.Context, project *applyconfigurationspr return nil, fmt.Errorf("project.Name must be provided to Apply") } obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(projectsResource, *name, types.ApplyPatchType, data), &projectv1.Project{}) + Invokes(testing.NewRootPatchSubresourceAction(projectsResource, *name, types.ApplyPatchType, data), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } // ApplyStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). -func (c *FakeProjects) ApplyStatus(ctx context.Context, project *applyconfigurationsprojectv1.ProjectApplyConfiguration, opts v1.ApplyOptions) (result *projectv1.Project, err error) { +func (c *FakeProjects) ApplyStatus(ctx context.Context, project *projectv1.ProjectApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Project, err error) { if project == nil { return nil, fmt.Errorf("project provided to Apply must not be nil") } @@ -155,9 +154,9 @@ func (c *FakeProjects) ApplyStatus(ctx context.Context, project *applyconfigurat return nil, fmt.Errorf("project.Name must be provided to Apply") } obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(projectsResource, *name, types.ApplyPatchType, data, "status"), &projectv1.Project{}) + Invokes(testing.NewRootPatchSubresourceAction(projectsResource, *name, types.ApplyPatchType, data, "status"), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } diff --git a/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_projectrequest.go b/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_projectrequest.go index 86aea37b4..579ba0fa4 100644 --- a/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_projectrequest.go +++ b/vendor/github.com/openshift/client-go/project/clientset/versioned/typed/project/v1/fake/fake_projectrequest.go @@ -7,10 +7,9 @@ import ( json "encoding/json" "fmt" - projectv1 "github.com/openshift/api/project/v1" - v1 "github.com/openshift/client-go/project/applyconfigurations/project/v1" + v1 "github.com/openshift/api/project/v1" + projectv1 "github.com/openshift/client-go/project/applyconfigurations/project/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - schema "k8s.io/apimachinery/pkg/runtime/schema" types "k8s.io/apimachinery/pkg/types" testing "k8s.io/client-go/testing" ) @@ -20,12 +19,12 @@ type FakeProjectRequests struct { Fake *FakeProjectV1 } -var projectrequestsResource = schema.GroupVersionResource{Group: "project.openshift.io", Version: "v1", Resource: "projectrequests"} +var projectrequestsResource = v1.SchemeGroupVersion.WithResource("projectrequests") -var projectrequestsKind = schema.GroupVersionKind{Group: "project.openshift.io", Version: "v1", Kind: "ProjectRequest"} +var projectrequestsKind = v1.SchemeGroupVersion.WithKind("ProjectRequest") // Apply takes the given apply declarative configuration, applies it and returns the applied projectRequest. -func (c *FakeProjectRequests) Apply(ctx context.Context, projectRequest *v1.ProjectRequestApplyConfiguration, opts metav1.ApplyOptions) (result *projectv1.ProjectRequest, err error) { +func (c *FakeProjectRequests) Apply(ctx context.Context, projectRequest *projectv1.ProjectRequestApplyConfiguration, opts metav1.ApplyOptions) (result *v1.ProjectRequest, err error) { if projectRequest == nil { return nil, fmt.Errorf("projectRequest provided to Apply must not be nil") } @@ -38,19 +37,19 @@ func (c *FakeProjectRequests) Apply(ctx context.Context, projectRequest *v1.Proj return nil, fmt.Errorf("projectRequest.Name must be provided to Apply") } obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(projectrequestsResource, *name, types.ApplyPatchType, data), &projectv1.ProjectRequest{}) + Invokes(testing.NewRootPatchSubresourceAction(projectrequestsResource, *name, types.ApplyPatchType, data), &v1.ProjectRequest{}) if obj == nil { return nil, err } - return obj.(*projectv1.ProjectRequest), err + return obj.(*v1.ProjectRequest), err } // Create takes the representation of a projectRequest and creates it. Returns the server's representation of the project, and an error, if there is any. -func (c *FakeProjectRequests) Create(ctx context.Context, projectRequest *projectv1.ProjectRequest, opts metav1.CreateOptions) (result *projectv1.Project, err error) { +func (c *FakeProjectRequests) Create(ctx context.Context, projectRequest *v1.ProjectRequest, opts metav1.CreateOptions) (result *v1.Project, err error) { obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(projectrequestsResource, projectRequest), &projectv1.Project{}) + Invokes(testing.NewRootCreateAction(projectrequestsResource, projectRequest), &v1.Project{}) if obj == nil { return nil, err } - return obj.(*projectv1.Project), err + return obj.(*v1.Project), err } diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/internal/internal.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/internal/internal.go index cc97c71ff..38acf6dc7 100644 --- a/vendor/github.com/openshift/client-go/route/applyconfigurations/internal/internal.go +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/internal/internal.go @@ -23,6 +23,13 @@ func Parser() *typed.Parser { var parserOnce sync.Once var parser *typed.Parser var schemaYAML = typed.YAMLObject(`types: +- name: com.github.openshift.api.route.v1.LocalObjectReference + map: + fields: + - name: name + type: + scalar: string + elementRelationship: atomic - name: com.github.openshift.api.route.v1.Route map: fields: @@ -44,6 +51,58 @@ var schemaYAML = typed.YAMLObject(`types: type: namedType: com.github.openshift.api.route.v1.RouteStatus default: {} +- name: com.github.openshift.api.route.v1.RouteHTTPHeader + map: + fields: + - name: action + type: + namedType: com.github.openshift.api.route.v1.RouteHTTPHeaderActionUnion + default: {} + - name: name + type: + scalar: string + default: "" +- name: com.github.openshift.api.route.v1.RouteHTTPHeaderActionUnion + map: + fields: + - name: set + type: + namedType: com.github.openshift.api.route.v1.RouteSetHTTPHeader + - name: type + type: + scalar: string + default: "" + unions: + - discriminator: type + fields: + - fieldName: set + discriminatorValue: Set +- name: com.github.openshift.api.route.v1.RouteHTTPHeaderActions + map: + fields: + - name: request + type: + list: + elementType: + namedType: com.github.openshift.api.route.v1.RouteHTTPHeader + elementRelationship: associative + keys: + - name + - name: response + type: + list: + elementType: + namedType: com.github.openshift.api.route.v1.RouteHTTPHeader + elementRelationship: associative + keys: + - name +- name: com.github.openshift.api.route.v1.RouteHTTPHeaders + map: + fields: + - name: actions + type: + namedType: com.github.openshift.api.route.v1.RouteHTTPHeaderActions + default: {} - name: com.github.openshift.api.route.v1.RouteIngress map: fields: @@ -92,6 +151,13 @@ var schemaYAML = typed.YAMLObject(`types: type: namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString default: {} +- name: com.github.openshift.api.route.v1.RouteSetHTTPHeader + map: + fields: + - name: value + type: + scalar: string + default: "" - name: com.github.openshift.api.route.v1.RouteSpec map: fields: @@ -104,6 +170,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: host type: scalar: string + - name: httpHeaders + type: + namedType: com.github.openshift.api.route.v1.RouteHTTPHeaders - name: path type: scalar: string @@ -158,6 +227,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: destinationCACertificate type: scalar: string + - name: externalCertificate + type: + namedType: com.github.openshift.api.route.v1.LocalObjectReference - name: insecureEdgeTerminationPolicy type: scalar: string diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/localobjectreference.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/localobjectreference.go new file mode 100644 index 000000000..da17405b0 --- /dev/null +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/localobjectreference.go @@ -0,0 +1,23 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// LocalObjectReferenceApplyConfiguration represents an declarative configuration of the LocalObjectReference type for use +// with apply. +type LocalObjectReferenceApplyConfiguration struct { + Name *string `json:"name,omitempty"` +} + +// LocalObjectReferenceApplyConfiguration constructs an declarative configuration of the LocalObjectReference type for use with +// apply. +func LocalObjectReference() *LocalObjectReferenceApplyConfiguration { + return &LocalObjectReferenceApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *LocalObjectReferenceApplyConfiguration) WithName(value string) *LocalObjectReferenceApplyConfiguration { + b.Name = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheader.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheader.go new file mode 100644 index 000000000..f06203cab --- /dev/null +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheader.go @@ -0,0 +1,32 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// RouteHTTPHeaderApplyConfiguration represents an declarative configuration of the RouteHTTPHeader type for use +// with apply. +type RouteHTTPHeaderApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Action *RouteHTTPHeaderActionUnionApplyConfiguration `json:"action,omitempty"` +} + +// RouteHTTPHeaderApplyConfiguration constructs an declarative configuration of the RouteHTTPHeader type for use with +// apply. +func RouteHTTPHeader() *RouteHTTPHeaderApplyConfiguration { + return &RouteHTTPHeaderApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *RouteHTTPHeaderApplyConfiguration) WithName(value string) *RouteHTTPHeaderApplyConfiguration { + b.Name = &value + return b +} + +// WithAction sets the Action field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Action field is set to the value of the last call. +func (b *RouteHTTPHeaderApplyConfiguration) WithAction(value *RouteHTTPHeaderActionUnionApplyConfiguration) *RouteHTTPHeaderApplyConfiguration { + b.Action = value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaderactions.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaderactions.go new file mode 100644 index 000000000..ef7fa5812 --- /dev/null +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaderactions.go @@ -0,0 +1,42 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// RouteHTTPHeaderActionsApplyConfiguration represents an declarative configuration of the RouteHTTPHeaderActions type for use +// with apply. +type RouteHTTPHeaderActionsApplyConfiguration struct { + Response []RouteHTTPHeaderApplyConfiguration `json:"response,omitempty"` + Request []RouteHTTPHeaderApplyConfiguration `json:"request,omitempty"` +} + +// RouteHTTPHeaderActionsApplyConfiguration constructs an declarative configuration of the RouteHTTPHeaderActions type for use with +// apply. +func RouteHTTPHeaderActions() *RouteHTTPHeaderActionsApplyConfiguration { + return &RouteHTTPHeaderActionsApplyConfiguration{} +} + +// WithResponse adds the given value to the Response field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Response field. +func (b *RouteHTTPHeaderActionsApplyConfiguration) WithResponse(values ...*RouteHTTPHeaderApplyConfiguration) *RouteHTTPHeaderActionsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithResponse") + } + b.Response = append(b.Response, *values[i]) + } + return b +} + +// WithRequest adds the given value to the Request field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Request field. +func (b *RouteHTTPHeaderActionsApplyConfiguration) WithRequest(values ...*RouteHTTPHeaderApplyConfiguration) *RouteHTTPHeaderActionsApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithRequest") + } + b.Request = append(b.Request, *values[i]) + } + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaderactionunion.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaderactionunion.go new file mode 100644 index 000000000..b786c07dc --- /dev/null +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaderactionunion.go @@ -0,0 +1,36 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/openshift/api/route/v1" +) + +// RouteHTTPHeaderActionUnionApplyConfiguration represents an declarative configuration of the RouteHTTPHeaderActionUnion type for use +// with apply. +type RouteHTTPHeaderActionUnionApplyConfiguration struct { + Type *v1.RouteHTTPHeaderActionType `json:"type,omitempty"` + Set *RouteSetHTTPHeaderApplyConfiguration `json:"set,omitempty"` +} + +// RouteHTTPHeaderActionUnionApplyConfiguration constructs an declarative configuration of the RouteHTTPHeaderActionUnion type for use with +// apply. +func RouteHTTPHeaderActionUnion() *RouteHTTPHeaderActionUnionApplyConfiguration { + return &RouteHTTPHeaderActionUnionApplyConfiguration{} +} + +// WithType sets the Type field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Type field is set to the value of the last call. +func (b *RouteHTTPHeaderActionUnionApplyConfiguration) WithType(value v1.RouteHTTPHeaderActionType) *RouteHTTPHeaderActionUnionApplyConfiguration { + b.Type = &value + return b +} + +// WithSet sets the Set field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Set field is set to the value of the last call. +func (b *RouteHTTPHeaderActionUnionApplyConfiguration) WithSet(value *RouteSetHTTPHeaderApplyConfiguration) *RouteHTTPHeaderActionUnionApplyConfiguration { + b.Set = value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaders.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaders.go new file mode 100644 index 000000000..e112230c8 --- /dev/null +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routehttpheaders.go @@ -0,0 +1,23 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// RouteHTTPHeadersApplyConfiguration represents an declarative configuration of the RouteHTTPHeaders type for use +// with apply. +type RouteHTTPHeadersApplyConfiguration struct { + Actions *RouteHTTPHeaderActionsApplyConfiguration `json:"actions,omitempty"` +} + +// RouteHTTPHeadersApplyConfiguration constructs an declarative configuration of the RouteHTTPHeaders type for use with +// apply. +func RouteHTTPHeaders() *RouteHTTPHeadersApplyConfiguration { + return &RouteHTTPHeadersApplyConfiguration{} +} + +// WithActions sets the Actions field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Actions field is set to the value of the last call. +func (b *RouteHTTPHeadersApplyConfiguration) WithActions(value *RouteHTTPHeaderActionsApplyConfiguration) *RouteHTTPHeadersApplyConfiguration { + b.Actions = value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routesethttpheader.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routesethttpheader.go new file mode 100644 index 000000000..4c0952c79 --- /dev/null +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routesethttpheader.go @@ -0,0 +1,23 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1 + +// RouteSetHTTPHeaderApplyConfiguration represents an declarative configuration of the RouteSetHTTPHeader type for use +// with apply. +type RouteSetHTTPHeaderApplyConfiguration struct { + Value *string `json:"value,omitempty"` +} + +// RouteSetHTTPHeaderApplyConfiguration constructs an declarative configuration of the RouteSetHTTPHeader type for use with +// apply. +func RouteSetHTTPHeader() *RouteSetHTTPHeaderApplyConfiguration { + return &RouteSetHTTPHeaderApplyConfiguration{} +} + +// WithValue sets the Value field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Value field is set to the value of the last call. +func (b *RouteSetHTTPHeaderApplyConfiguration) WithValue(value string) *RouteSetHTTPHeaderApplyConfiguration { + b.Value = &value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routespec.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routespec.go index 8034bace0..c6dcacfb2 100644 --- a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routespec.go +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/routespec.go @@ -17,6 +17,7 @@ type RouteSpecApplyConfiguration struct { Port *RoutePortApplyConfiguration `json:"port,omitempty"` TLS *TLSConfigApplyConfiguration `json:"tls,omitempty"` WildcardPolicy *routev1.WildcardPolicyType `json:"wildcardPolicy,omitempty"` + HTTPHeaders *RouteHTTPHeadersApplyConfiguration `json:"httpHeaders,omitempty"` } // RouteSpecApplyConfiguration constructs an declarative configuration of the RouteSpec type for use with @@ -93,3 +94,11 @@ func (b *RouteSpecApplyConfiguration) WithWildcardPolicy(value routev1.WildcardP b.WildcardPolicy = &value return b } + +// WithHTTPHeaders sets the HTTPHeaders field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HTTPHeaders field is set to the value of the last call. +func (b *RouteSpecApplyConfiguration) WithHTTPHeaders(value *RouteHTTPHeadersApplyConfiguration) *RouteSpecApplyConfiguration { + b.HTTPHeaders = value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/tlsconfig.go b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/tlsconfig.go index 5188a3921..9f5f4449c 100644 --- a/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/tlsconfig.go +++ b/vendor/github.com/openshift/client-go/route/applyconfigurations/route/v1/tlsconfig.go @@ -9,12 +9,13 @@ import ( // TLSConfigApplyConfiguration represents an declarative configuration of the TLSConfig type for use // with apply. type TLSConfigApplyConfiguration struct { - Termination *v1.TLSTerminationType `json:"termination,omitempty"` - Certificate *string `json:"certificate,omitempty"` - Key *string `json:"key,omitempty"` - CACertificate *string `json:"caCertificate,omitempty"` - DestinationCACertificate *string `json:"destinationCACertificate,omitempty"` - InsecureEdgeTerminationPolicy *v1.InsecureEdgeTerminationPolicyType `json:"insecureEdgeTerminationPolicy,omitempty"` + Termination *v1.TLSTerminationType `json:"termination,omitempty"` + Certificate *string `json:"certificate,omitempty"` + Key *string `json:"key,omitempty"` + CACertificate *string `json:"caCertificate,omitempty"` + DestinationCACertificate *string `json:"destinationCACertificate,omitempty"` + InsecureEdgeTerminationPolicy *v1.InsecureEdgeTerminationPolicyType `json:"insecureEdgeTerminationPolicy,omitempty"` + ExternalCertificate *LocalObjectReferenceApplyConfiguration `json:"externalCertificate,omitempty"` } // TLSConfigApplyConfiguration constructs an declarative configuration of the TLSConfig type for use with @@ -70,3 +71,11 @@ func (b *TLSConfigApplyConfiguration) WithInsecureEdgeTerminationPolicy(value v1 b.InsecureEdgeTerminationPolicy = &value return b } + +// WithExternalCertificate sets the ExternalCertificate field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ExternalCertificate field is set to the value of the last call. +func (b *TLSConfigApplyConfiguration) WithExternalCertificate(value *LocalObjectReferenceApplyConfiguration) *TLSConfigApplyConfiguration { + b.ExternalCertificate = value + return b +} diff --git a/vendor/github.com/openshift/client-go/route/clientset/versioned/clientset.go b/vendor/github.com/openshift/client-go/route/clientset/versioned/clientset.go index 2dfd30b81..23c80b601 100644 --- a/vendor/github.com/openshift/client-go/route/clientset/versioned/clientset.go +++ b/vendor/github.com/openshift/client-go/route/clientset/versioned/clientset.go @@ -17,8 +17,7 @@ type Interface interface { RouteV1() routev1.RouteV1Interface } -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. +// Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient routeV1 *routev1.RouteV1Client diff --git a/vendor/github.com/openshift/client-go/route/clientset/versioned/doc.go b/vendor/github.com/openshift/client-go/route/clientset/versioned/doc.go deleted file mode 100644 index 0e0c2a890..000000000 --- a/vendor/github.com/openshift/client-go/route/clientset/versioned/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/vendor/github.com/openshift/client-go/route/clientset/versioned/fake/register.go b/vendor/github.com/openshift/client-go/route/clientset/versioned/fake/register.go index 279f25d9d..21e116a50 100644 --- a/vendor/github.com/openshift/client-go/route/clientset/versioned/fake/register.go +++ b/vendor/github.com/openshift/client-go/route/clientset/versioned/fake/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/vendor/github.com/openshift/client-go/route/clientset/versioned/scheme/register.go b/vendor/github.com/openshift/client-go/route/clientset/versioned/scheme/register.go index 0604e5613..53ac82ff5 100644 --- a/vendor/github.com/openshift/client-go/route/clientset/versioned/scheme/register.go +++ b/vendor/github.com/openshift/client-go/route/clientset/versioned/scheme/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/vendor/github.com/openshift/client-go/route/clientset/versioned/typed/route/v1/fake/fake_route.go b/vendor/github.com/openshift/client-go/route/clientset/versioned/typed/route/v1/fake/fake_route.go index 6f41ab132..734c8b712 100644 --- a/vendor/github.com/openshift/client-go/route/clientset/versioned/typed/route/v1/fake/fake_route.go +++ b/vendor/github.com/openshift/client-go/route/clientset/versioned/typed/route/v1/fake/fake_route.go @@ -7,11 +7,10 @@ import ( json "encoding/json" "fmt" - routev1 "github.com/openshift/api/route/v1" - applyconfigurationsroutev1 "github.com/openshift/client-go/route/applyconfigurations/route/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "github.com/openshift/api/route/v1" + routev1 "github.com/openshift/client-go/route/applyconfigurations/route/v1" + metav1 "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" @@ -23,25 +22,25 @@ type FakeRoutes struct { ns string } -var routesResource = schema.GroupVersionResource{Group: "route.openshift.io", Version: "v1", Resource: "routes"} +var routesResource = v1.SchemeGroupVersion.WithResource("routes") -var routesKind = schema.GroupVersionKind{Group: "route.openshift.io", Version: "v1", Kind: "Route"} +var routesKind = v1.SchemeGroupVersion.WithKind("Route") // Get takes name of the route, and returns the corresponding route object, and an error if there is any. -func (c *FakeRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *routev1.Route, err error) { +func (c *FakeRoutes) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Route, err error) { obj, err := c.Fake. - Invokes(testing.NewGetAction(routesResource, c.ns, name), &routev1.Route{}) + Invokes(testing.NewGetAction(routesResource, c.ns, name), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), err } // List takes label and field selectors, and returns the list of Routes that match those selectors. -func (c *FakeRoutes) List(ctx context.Context, opts v1.ListOptions) (result *routev1.RouteList, err error) { +func (c *FakeRoutes) List(ctx context.Context, opts metav1.ListOptions) (result *v1.RouteList, err error) { obj, err := c.Fake. - Invokes(testing.NewListAction(routesResource, routesKind, c.ns, opts), &routev1.RouteList{}) + Invokes(testing.NewListAction(routesResource, routesKind, c.ns, opts), &v1.RouteList{}) if obj == nil { return nil, err @@ -51,8 +50,8 @@ func (c *FakeRoutes) List(ctx context.Context, opts v1.ListOptions) (result *rou if label == nil { label = labels.Everything() } - list := &routev1.RouteList{ListMeta: obj.(*routev1.RouteList).ListMeta} - for _, item := range obj.(*routev1.RouteList).Items { + list := &v1.RouteList{ListMeta: obj.(*v1.RouteList).ListMeta} + for _, item := range obj.(*v1.RouteList).Items { if label.Matches(labels.Set(item.Labels)) { list.Items = append(list.Items, item) } @@ -61,75 +60,75 @@ func (c *FakeRoutes) List(ctx context.Context, opts v1.ListOptions) (result *rou } // Watch returns a watch.Interface that watches the requested routes. -func (c *FakeRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { +func (c *FakeRoutes) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(routesResource, c.ns, opts)) } // Create takes the representation of a route and creates it. Returns the server's representation of the route, and an error, if there is any. -func (c *FakeRoutes) Create(ctx context.Context, route *routev1.Route, opts v1.CreateOptions) (result *routev1.Route, err error) { +func (c *FakeRoutes) Create(ctx context.Context, route *v1.Route, opts metav1.CreateOptions) (result *v1.Route, err error) { obj, err := c.Fake. - Invokes(testing.NewCreateAction(routesResource, c.ns, route), &routev1.Route{}) + Invokes(testing.NewCreateAction(routesResource, c.ns, route), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), err } // Update takes the representation of a route and updates it. Returns the server's representation of the route, and an error, if there is any. -func (c *FakeRoutes) Update(ctx context.Context, route *routev1.Route, opts v1.UpdateOptions) (result *routev1.Route, err error) { +func (c *FakeRoutes) Update(ctx context.Context, route *v1.Route, opts metav1.UpdateOptions) (result *v1.Route, err error) { obj, err := c.Fake. - Invokes(testing.NewUpdateAction(routesResource, c.ns, route), &routev1.Route{}) + Invokes(testing.NewUpdateAction(routesResource, c.ns, route), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), 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 *FakeRoutes) UpdateStatus(ctx context.Context, route *routev1.Route, opts v1.UpdateOptions) (*routev1.Route, error) { +func (c *FakeRoutes) UpdateStatus(ctx context.Context, route *v1.Route, opts metav1.UpdateOptions) (*v1.Route, error) { obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(routesResource, "status", c.ns, route), &routev1.Route{}) + Invokes(testing.NewUpdateSubresourceAction(routesResource, "status", c.ns, route), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), err } // Delete takes name of the route and deletes it. Returns an error if one occurs. -func (c *FakeRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { +func (c *FakeRoutes) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(routesResource, c.ns, name, opts), &routev1.Route{}) + Invokes(testing.NewDeleteActionWithOptions(routesResource, c.ns, name, opts), &v1.Route{}) return err } // DeleteCollection deletes a collection of objects. -func (c *FakeRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { +func (c *FakeRoutes) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { action := testing.NewDeleteCollectionAction(routesResource, c.ns, listOpts) - _, err := c.Fake.Invokes(action, &routev1.RouteList{}) + _, err := c.Fake.Invokes(action, &v1.RouteList{}) return err } // Patch applies the patch and returns the patched route. -func (c *FakeRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *routev1.Route, err error) { +func (c *FakeRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Route, err error) { obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(routesResource, c.ns, name, pt, data, subresources...), &routev1.Route{}) + Invokes(testing.NewPatchSubresourceAction(routesResource, c.ns, name, pt, data, subresources...), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), err } // Apply takes the given apply declarative configuration, applies it and returns the applied route. -func (c *FakeRoutes) Apply(ctx context.Context, route *applyconfigurationsroutev1.RouteApplyConfiguration, opts v1.ApplyOptions) (result *routev1.Route, err error) { +func (c *FakeRoutes) Apply(ctx context.Context, route *routev1.RouteApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Route, err error) { if route == nil { return nil, fmt.Errorf("route provided to Apply must not be nil") } @@ -142,17 +141,17 @@ func (c *FakeRoutes) Apply(ctx context.Context, route *applyconfigurationsroutev return nil, fmt.Errorf("route.Name must be provided to Apply") } obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(routesResource, c.ns, *name, types.ApplyPatchType, data), &routev1.Route{}) + Invokes(testing.NewPatchSubresourceAction(routesResource, c.ns, *name, types.ApplyPatchType, data), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), err } // ApplyStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). -func (c *FakeRoutes) ApplyStatus(ctx context.Context, route *applyconfigurationsroutev1.RouteApplyConfiguration, opts v1.ApplyOptions) (result *routev1.Route, err error) { +func (c *FakeRoutes) ApplyStatus(ctx context.Context, route *routev1.RouteApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Route, err error) { if route == nil { return nil, fmt.Errorf("route provided to Apply must not be nil") } @@ -165,10 +164,10 @@ func (c *FakeRoutes) ApplyStatus(ctx context.Context, route *applyconfigurations return nil, fmt.Errorf("route.Name must be provided to Apply") } obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(routesResource, c.ns, *name, types.ApplyPatchType, data, "status"), &routev1.Route{}) + Invokes(testing.NewPatchSubresourceAction(routesResource, c.ns, *name, types.ApplyPatchType, data, "status"), &v1.Route{}) if obj == nil { return nil, err } - return obj.(*routev1.Route), err + return obj.(*v1.Route), err } diff --git a/vendor/github.com/openshift/library-go/pkg/authorization/authorizationutil/subject.go b/vendor/github.com/openshift/library-go/pkg/authorization/authorizationutil/subject.go new file mode 100644 index 000000000..74c179e68 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/authorization/authorizationutil/subject.go @@ -0,0 +1,56 @@ +package authorizationutil + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apiserver/pkg/authentication/serviceaccount" +) + +func BuildRBACSubjects(users, groups []string) []rbacv1.Subject { + subjects := []rbacv1.Subject{} + + for _, user := range users { + saNamespace, saName, err := serviceaccount.SplitUsername(user) + if err == nil { + subjects = append(subjects, rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Namespace: saNamespace, Name: saName}) + } else { + subjects = append(subjects, rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: user}) + } + } + + for _, group := range groups { + subjects = append(subjects, rbacv1.Subject{Kind: rbacv1.GroupKind, APIGroup: rbacv1.GroupName, Name: group}) + } + + return subjects +} + +func RBACSubjectsToUsersAndGroups(subjects []rbacv1.Subject, defaultNamespace string) (users []string, groups []string) { + for _, subject := range subjects { + + switch { + case subject.APIGroup == rbacv1.GroupName && subject.Kind == rbacv1.GroupKind: + groups = append(groups, subject.Name) + case subject.APIGroup == rbacv1.GroupName && subject.Kind == rbacv1.UserKind: + users = append(users, subject.Name) + case subject.APIGroup == "" && subject.Kind == rbacv1.ServiceAccountKind: + // default the namespace to namespace we're working in if + // it's available. This allows rolebindings that reference + // SAs in the local namespace to avoid having to qualify + // them. + ns := defaultNamespace + if len(subject.Namespace) > 0 { + ns = subject.Namespace + } + if len(ns) > 0 { + name := serviceaccount.MakeUsername(ns, subject.Name) + users = append(users, name) + } else { + // maybe error? this fails safe at any rate + } + default: + // maybe error? This fails safe at any rate + } + } + + return users, groups +} diff --git a/vendor/github.com/openshift/library-go/pkg/authorization/authorizationutil/util.go b/vendor/github.com/openshift/library-go/pkg/authorization/authorizationutil/util.go new file mode 100644 index 000000000..040d0f643 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/authorization/authorizationutil/util.go @@ -0,0 +1,50 @@ +package authorizationutil + +import ( + "context" + "errors" + + authorizationv1 "k8s.io/api/authorization/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/authentication/user" + authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1" +) + +// AddUserToSAR adds the requisite user information to a SubjectAccessReview. +// It returns the modified SubjectAccessReview. +func AddUserToSAR(user user.Info, sar *authorizationv1.SubjectAccessReview) *authorizationv1.SubjectAccessReview { + sar.Spec.User = user.GetName() + // reminiscent of the bad old days of C. Copies copy the min number of elements of both source and dest + sar.Spec.Groups = make([]string, len(user.GetGroups())) + copy(sar.Spec.Groups, user.GetGroups()) + sar.Spec.Extra = map[string]authorizationv1.ExtraValue{} + + for k, v := range user.GetExtra() { + sar.Spec.Extra[k] = authorizationv1.ExtraValue(v) + } + + return sar +} + +// Authorize verifies that a given user is permitted to carry out a given +// action. If this cannot be determined, or if the user is not permitted, an +// error is returned. +func Authorize(sarClient authorizationclient.SubjectAccessReviewInterface, user user.Info, resourceAttributes *authorizationv1.ResourceAttributes) error { + sar := AddUserToSAR(user, &authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + ResourceAttributes: resourceAttributes, + }, + }) + + resp, err := sarClient.Create(context.TODO(), sar, metav1.CreateOptions{}) + if err == nil && resp != nil && resp.Status.Allowed { + return nil + } + + if err == nil { + err = errors.New(resp.Status.Reason) + } + return kerrors.NewForbidden(schema.GroupResource{Group: resourceAttributes.Group, Resource: resourceAttributes.Resource}, resourceAttributes.Name, err) +} diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go b/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go index 554112c49..63184d2eb 100644 --- a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go +++ b/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go @@ -371,7 +371,7 @@ func (c *TLSCertificateConfig) GetPEMBytes() ([]byte, []byte, error) { if err != nil { return nil, nil, err } - keyBytes, err := encodeKey(c.Key) + keyBytes, err := EncodeKey(c.Key) if err != nil { return nil, nil, err } @@ -644,15 +644,20 @@ func MakeSelfSignedCAConfigForSubject(subject pkix.Name, expireDays int) (*TLSCe } caLifetime := time.Duration(caLifetimeInDays) * 24 * time.Hour - return makeSelfSignedCAConfigForSubjectAndDuration(subject, caLifetime) + return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime) } func MakeSelfSignedCAConfigForDuration(name string, caLifetime time.Duration) (*TLSCertificateConfig, error) { subject := pkix.Name{CommonName: name} - return makeSelfSignedCAConfigForSubjectAndDuration(subject, caLifetime) + return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime) } -func makeSelfSignedCAConfigForSubjectAndDuration(subject pkix.Name, caLifetime time.Duration) (*TLSCertificateConfig, error) { +func UnsafeMakeSelfSignedCAConfigForDurationAtTime(name string, currentTime func() time.Time, caLifetime time.Duration) (*TLSCertificateConfig, error) { + subject := pkix.Name{CommonName: name} + return makeSelfSignedCAConfigForSubjectAndDuration(subject, currentTime, caLifetime) +} + +func makeSelfSignedCAConfigForSubjectAndDuration(subject pkix.Name, currentTime func() time.Time, caLifetime time.Duration) (*TLSCertificateConfig, error) { // Create CA cert rootcaPublicKey, rootcaPrivateKey, publicKeyHash, err := newKeyPairWithHash() if err != nil { @@ -661,7 +666,7 @@ func makeSelfSignedCAConfigForSubjectAndDuration(subject pkix.Name, caLifetime t // AuthorityKeyId and SubjectKeyId should match for a self-signed CA authorityKeyId := publicKeyHash subjectKeyId := publicKeyHash - rootcaTemplate := newSigningCertificateTemplateForDuration(subject, caLifetime, time.Now, authorityKeyId, subjectKeyId) + rootcaTemplate := newSigningCertificateTemplateForDuration(subject, caLifetime, currentTime, authorityKeyId, subjectKeyId) rootcaCert, err := signCertificate(rootcaTemplate, rootcaPublicKey, rootcaTemplate, rootcaPrivateKey) if err != nil { return nil, err @@ -682,7 +687,7 @@ func MakeCAConfigForDuration(name string, caLifetime time.Duration, issuer *CA) authorityKeyId := issuer.Config.Certs[0].SubjectKeyId subjectKeyId := publicKeyHash signerTemplate := newSigningCertificateTemplateForDuration(pkix.Name{CommonName: name}, caLifetime, time.Now, authorityKeyId, subjectKeyId) - signerCert, err := issuer.signCertificate(signerTemplate, signerPublicKey) + signerCert, err := issuer.SignCertificate(signerTemplate, signerPublicKey) if err != nil { return nil, err } @@ -741,7 +746,7 @@ func (ca *CA) MakeAndWriteSubCA(certFile, keyFile, serialFile, name string, expi }, nil } -func (ca *CA) EnsureServerCert(certFile, keyFile string, hostnames sets.String, expireDays int) (*TLSCertificateConfig, bool, error) { +func (ca *CA) EnsureServerCert(certFile, keyFile string, hostnames sets.Set[string], expireDays int) (*TLSCertificateConfig, bool, error) { certConfig, err := GetServerCert(certFile, keyFile, hostnames) if err != nil { certConfig, err = ca.MakeAndWriteServerCert(certFile, keyFile, hostnames, expireDays) @@ -751,25 +756,27 @@ func (ca *CA) EnsureServerCert(certFile, keyFile string, hostnames sets.String, return certConfig, false, nil } -func GetServerCert(certFile, keyFile string, hostnames sets.String) (*TLSCertificateConfig, error) { +func GetServerCert(certFile, keyFile string, hostnames sets.Set[string]) (*TLSCertificateConfig, error) { server, err := GetTLSCertificateConfig(certFile, keyFile) if err != nil { return nil, err } cert := server.Certs[0] - ips, dns := IPAddressesDNSNames(hostnames.List()) - missingIps := ipsNotInSlice(ips, cert.IPAddresses) - missingDns := stringsNotInSlice(dns, cert.DNSNames) - if len(missingIps) == 0 && len(missingDns) == 0 { + certNames := sets.New[string]() + for _, ip := range cert.IPAddresses { + certNames.Insert(ip.String()) + } + certNames.Insert(cert.DNSNames...) + if hostnames.Equal(certNames) { klog.V(4).Infof("Found existing server certificate in %s", certFile) return server, nil } - return nil, fmt.Errorf("Existing server certificate in %s was missing some hostnames (%v) or IP addresses (%v).", certFile, missingDns, missingIps) + return nil, fmt.Errorf("Existing server certificate in %s does not match required hostnames.", certFile) } -func (ca *CA) MakeAndWriteServerCert(certFile, keyFile string, hostnames sets.String, expireDays int) (*TLSCertificateConfig, error) { +func (ca *CA) MakeAndWriteServerCert(certFile, keyFile string, hostnames sets.Set[string], expireDays int) (*TLSCertificateConfig, error) { klog.V(4).Infof("Generating server certificate in %s, key in %s", certFile, keyFile) server, err := ca.MakeServerCert(hostnames, expireDays) @@ -786,17 +793,17 @@ func (ca *CA) MakeAndWriteServerCert(certFile, keyFile string, hostnames sets.St // if the extension attempt failed. type CertificateExtensionFunc func(*x509.Certificate) error -func (ca *CA) MakeServerCert(hostnames sets.String, expireDays int, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { +func (ca *CA) MakeServerCert(hostnames sets.Set[string], expireDays int, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { serverPublicKey, serverPrivateKey, publicKeyHash, _ := newKeyPairWithHash() authorityKeyId := ca.Config.Certs[0].SubjectKeyId subjectKeyId := publicKeyHash - serverTemplate := newServerCertificateTemplate(pkix.Name{CommonName: hostnames.List()[0]}, hostnames.List(), expireDays, time.Now, authorityKeyId, subjectKeyId) + serverTemplate := newServerCertificateTemplate(pkix.Name{CommonName: sets.List(hostnames)[0]}, sets.List(hostnames), expireDays, time.Now, authorityKeyId, subjectKeyId) for _, fn := range fns { if err := fn(serverTemplate); err != nil { return nil, err } } - serverCrt, err := ca.signCertificate(serverTemplate, serverPublicKey) + serverCrt, err := ca.SignCertificate(serverTemplate, serverPublicKey) if err != nil { return nil, err } @@ -807,17 +814,17 @@ func (ca *CA) MakeServerCert(hostnames sets.String, expireDays int, fns ...Certi return server, nil } -func (ca *CA) MakeServerCertForDuration(hostnames sets.String, lifetime time.Duration, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { +func (ca *CA) MakeServerCertForDuration(hostnames sets.Set[string], lifetime time.Duration, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { serverPublicKey, serverPrivateKey, publicKeyHash, _ := newKeyPairWithHash() authorityKeyId := ca.Config.Certs[0].SubjectKeyId subjectKeyId := publicKeyHash - serverTemplate := newServerCertificateTemplateForDuration(pkix.Name{CommonName: hostnames.List()[0]}, hostnames.List(), lifetime, time.Now, authorityKeyId, subjectKeyId) + serverTemplate := newServerCertificateTemplateForDuration(pkix.Name{CommonName: sets.List(hostnames)[0]}, sets.List(hostnames), lifetime, time.Now, authorityKeyId, subjectKeyId) for _, fn := range fns { if err := fn(serverTemplate); err != nil { return nil, err } } - serverCrt, err := ca.signCertificate(serverTemplate, serverPublicKey) + serverCrt, err := ca.SignCertificate(serverTemplate, serverPublicKey) if err != nil { return nil, err } @@ -843,7 +850,7 @@ func GetClientCertificate(certFile, keyFile string, u user.Info) (*TLSCertificat return nil, err } - if subject := certConfig.Certs[0].Subject; subjectChanged(subject, userToSubject(u)) { + if subject := certConfig.Certs[0].Subject; subjectChanged(subject, UserToSubject(u)) { return nil, fmt.Errorf("existing client certificate in %s was issued for a different Subject (%s)", certFile, subject) } @@ -871,8 +878,8 @@ func (ca *CA) MakeClientCertificate(certFile, keyFile string, u user.Info, expir } clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate := newClientCertificateTemplate(userToSubject(u), expireDays, time.Now) - clientCrt, err := ca.signCertificate(clientTemplate, clientPublicKey) + clientTemplate := NewClientCertificateTemplate(UserToSubject(u), expireDays, time.Now) + clientCrt, err := ca.SignCertificate(clientTemplate, clientPublicKey) if err != nil { return nil, err } @@ -881,7 +888,7 @@ func (ca *CA) MakeClientCertificate(certFile, keyFile string, u user.Info, expir if err != nil { return nil, err } - keyData, err := encodeKey(clientPrivateKey) + keyData, err := EncodeKey(clientPrivateKey) if err != nil { return nil, err } @@ -898,8 +905,8 @@ func (ca *CA) MakeClientCertificate(certFile, keyFile string, u user.Info, expir func (ca *CA) MakeClientCertificateForDuration(u user.Info, lifetime time.Duration) (*TLSCertificateConfig, error) { clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate := newClientCertificateTemplateForDuration(userToSubject(u), lifetime, time.Now) - clientCrt, err := ca.signCertificate(clientTemplate, clientPublicKey) + clientTemplate := NewClientCertificateTemplateForDuration(UserToSubject(u), lifetime, time.Now) + clientCrt, err := ca.SignCertificate(clientTemplate, clientPublicKey) if err != nil { return nil, err } @@ -908,7 +915,7 @@ func (ca *CA) MakeClientCertificateForDuration(u user.Info, lifetime time.Durati if err != nil { return nil, err } - keyData, err := encodeKey(clientPrivateKey) + keyData, err := EncodeKey(clientPrivateKey) if err != nil { return nil, err } @@ -933,7 +940,7 @@ func (s sortedForDER) Less(i, j int) bool { return l1 < l2 } -func userToSubject(u user.Info) pkix.Name { +func UserToSubject(u user.Info) pkix.Name { // Ok we are going to order groups in a peculiar way here to workaround a // 2 bugs, 1 in golang (https://github.com/golang/go/issues/24254) which // incorrectly encodes Multivalued RDNs and another in GNUTLS clients @@ -959,7 +966,7 @@ func userToSubject(u user.Info) pkix.Name { } } -func (ca *CA) signCertificate(template *x509.Certificate, requestKey crypto.PublicKey) (*x509.Certificate, error) { +func (ca *CA) SignCertificate(template *x509.Certificate, requestKey crypto.PublicKey) (*x509.Certificate, error) { // Increment and persist serial serial, err := ca.SerialGenerator.Next(template) if err != nil { @@ -1106,7 +1113,7 @@ func CertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) { } // Can be used as a certificate in http.Transport TLSClientConfig -func newClientCertificateTemplate(subject pkix.Name, expireDays int, currentTime func() time.Time) *x509.Certificate { +func NewClientCertificateTemplate(subject pkix.Name, expireDays int, currentTime func() time.Time) *x509.Certificate { var lifetimeInDays = DefaultCertificateLifetimeInDays if expireDays > 0 { lifetimeInDays = expireDays @@ -1118,11 +1125,11 @@ func newClientCertificateTemplate(subject pkix.Name, expireDays int, currentTime lifetime := time.Duration(lifetimeInDays) * 24 * time.Hour - return newClientCertificateTemplateForDuration(subject, lifetime, currentTime) + return NewClientCertificateTemplateForDuration(subject, lifetime, currentTime) } // Can be used as a certificate in http.Transport TLSClientConfig -func newClientCertificateTemplateForDuration(subject pkix.Name, lifetime time.Duration, currentTime func() time.Time) *x509.Certificate { +func NewClientCertificateTemplateForDuration(subject pkix.Name, lifetime time.Duration, currentTime func() time.Time) *x509.Certificate { return &x509.Certificate{ Subject: subject, @@ -1168,7 +1175,7 @@ func EncodeCertificates(certs ...*x509.Certificate) ([]byte, error) { } return b.Bytes(), nil } -func encodeKey(key crypto.PrivateKey) ([]byte, error) { +func EncodeKey(key crypto.PrivateKey) ([]byte, error) { b := bytes.Buffer{} switch key := key.(type) { case *ecdsa.PrivateKey: @@ -1202,7 +1209,7 @@ func writeCertificates(f io.Writer, certs ...*x509.Certificate) error { return nil } func writeKeyFile(f io.Writer, key crypto.PrivateKey) error { - bytes, err := encodeKey(key) + bytes, err := EncodeKey(key) if err != nil { return err } @@ -1212,41 +1219,3 @@ func writeKeyFile(f io.Writer, key crypto.PrivateKey) error { return nil } - -func stringsNotInSlice(needles []string, haystack []string) []string { - missing := []string{} - for _, needle := range needles { - if !stringInSlice(needle, haystack) { - missing = append(missing, needle) - } - } - return missing -} - -func stringInSlice(needle string, haystack []string) bool { - for _, straw := range haystack { - if needle == straw { - return true - } - } - return false -} - -func ipsNotInSlice(needles []net.IP, haystack []net.IP) []net.IP { - missing := []net.IP{} - for _, needle := range needles { - if !ipInSlice(needle, haystack) { - missing = append(missing, needle) - } - } - return missing -} - -func ipInSlice(needle net.IP, haystack []net.IP) bool { - for _, straw := range haystack { - if needle.Equal(straw) { - return true - } - } - return false -} diff --git a/vendor/github.com/openshift/library-go/pkg/proc/proc_linux.go b/vendor/github.com/openshift/library-go/pkg/proc/proc_linux.go index d8ecd6349..fb8c7745d 100644 --- a/vendor/github.com/openshift/library-go/pkg/proc/proc_linux.go +++ b/vendor/github.com/openshift/library-go/pkg/proc/proc_linux.go @@ -2,7 +2,6 @@ package proc import ( "bufio" - "io/ioutil" "os" "path/filepath" "strconv" @@ -16,7 +15,7 @@ import ( // parseProcForZombies parses the current procfs mounted at /proc // to find processes in the zombie state. func parseProcForZombies() ([]int, error) { - files, err := ioutil.ReadDir("/proc") + files, err := os.ReadDir("/proc") if err != nil { return nil, err } diff --git a/vendor/github.com/openshift/library-go/pkg/route/secretmanager/fake/fake_manager.go b/vendor/github.com/openshift/library-go/pkg/route/secretmanager/fake/fake_manager.go new file mode 100644 index 000000000..2e1fca2de --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/route/secretmanager/fake/fake_manager.go @@ -0,0 +1,33 @@ +package fake + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" +) + +type SecretManager struct { + Err error + Secret *corev1.Secret + IsRegistered bool +} + +func (m *SecretManager) RegisterRoute(ctx context.Context, namespace string, routeName string, secretName string, handler cache.ResourceEventHandlerFuncs) error { + return m.Err +} +func (m *SecretManager) UnregisterRoute(namespace string, routeName string) error { + return m.Err +} + +func (m *SecretManager) GetSecret(ctx context.Context, namespace string, routeName string) (*corev1.Secret, error) { + return m.Secret, m.Err +} +func (m *SecretManager) IsRouteRegistered(namespace string, routeName string) bool { + return m.IsRegistered +} + +func (m *SecretManager) Queue() workqueue.RateLimitingInterface { + return nil +} diff --git a/vendor/github.com/openshift/library-go/pkg/route/secretmanager/manager.go b/vendor/github.com/openshift/library-go/pkg/route/secretmanager/manager.go new file mode 100644 index 000000000..7a3974748 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/route/secretmanager/manager.go @@ -0,0 +1,147 @@ +package secretmanager + +import ( + "context" + "fmt" + "sync" + + "github.com/openshift/library-go/pkg/secret" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog/v2" +) + +type SecretManager interface { + RegisterRoute(ctx context.Context, namespace string, routeName string, secretName string, handler cache.ResourceEventHandlerFuncs) error + UnregisterRoute(namespace string, routeName string) error + GetSecret(ctx context.Context, namespace string, routeName string) (*v1.Secret, error) + IsRouteRegistered(namespace string, routeName string) bool + Queue() workqueue.RateLimitingInterface +} + +// Manager is responsible for managing secrets associated with routes. It implements SecretManager. +type manager struct { + // monitor for managing and watching "single" secret dynamically. + monitor secret.SecretMonitor + + // Map of registered handlers for each route. + // Populated inside RegisterRoute() and used in UnregisterRoute(), GetSecret. + // generateKey() will create the map key. + registeredHandlers map[string]secret.SecretEventHandlerRegistration + + // Lock to protect access to registeredHandlers map. + handlersLock sync.RWMutex + + // Work queue to be used by the consumer of this Manager, mostly to add secret change events. + queue workqueue.RateLimitingInterface +} + +func NewManager(kubeClient kubernetes.Interface, queue workqueue.RateLimitingInterface) SecretManager { + return &manager{ + monitor: secret.NewSecretMonitor(kubeClient), + handlersLock: sync.RWMutex{}, + queue: queue, + registeredHandlers: make(map[string]secret.SecretEventHandlerRegistration), + } +} + +// Queue returns the work queue for the manager. +func (m *manager) Queue() workqueue.RateLimitingInterface { + return m.queue +} + +// RegisterRoute registers a route with a secret, enabling the manager to watch for the secret changes and associate them with the handler functions. +// Returns an error if the route is already registered with a secret or if adding the secret event handler fails. +func (m *manager) RegisterRoute(ctx context.Context, namespace, routeName, secretName string, handler cache.ResourceEventHandlerFuncs) error { + m.handlersLock.Lock() + defer m.handlersLock.Unlock() + + // Generate a unique key for the provided namespace and routeName. + key := generateKey(namespace, routeName) + + // Check if the route is already registered with the given key. + // Each route (namespace/routeName) should be registered only once with any secret. + // Note: inside a namespace multiple different routes can be registered(watch) with a common secret. + if _, exists := m.registeredHandlers[key]; exists { + return fmt.Errorf("route already registered with key %s", key) + } + + // Add a secret event handler for the specified namespace and secret, with the handler functions. + klog.V(5).Infof("trying to add handler for key %s with secret %s", key, secretName) + handlerRegistration, err := m.monitor.AddSecretEventHandler(ctx, namespace, secretName, handler) + if err != nil { + return err + } + + // Store the registration in the manager's map. Used during UnregisterRoute() and GetSecret(). + m.registeredHandlers[key] = handlerRegistration + klog.Infof("secret manager registered route for key %s with secret %s", key, secretName) + + return nil +} + +// UnregisterRoute removes the registration of a route from the manager. +// It removes the secret event handler from secret monitor and deletes its associated handler from manager's map. +func (m *manager) UnregisterRoute(namespace, routeName string) error { + m.handlersLock.Lock() + defer m.handlersLock.Unlock() + + key := generateKey(namespace, routeName) + + // Get the registered handler. + handlerRegistration, exists := m.registeredHandlers[key] + if !exists { + return fmt.Errorf("no handler registered with key %s", key) + } + + // Remove the corresponding secret event handler from the secret monitor. + klog.V(5).Info("trying to remove handler with key", key) + err := m.monitor.RemoveSecretEventHandler(handlerRegistration) + if err != nil { + return err + } + + // delete the registered handler from manager's map of handlers. + delete(m.registeredHandlers, key) + klog.Infof("secret manager unregistered route for key %s", key) + + return nil +} + +// GetSecret retrieves the secret object registered with a route. +func (m *manager) GetSecret(ctx context.Context, namespace, routeName string) (*v1.Secret, error) { + m.handlersLock.RLock() + defer m.handlersLock.RUnlock() + + key := generateKey(namespace, routeName) + + handlerRegistration, exists := m.registeredHandlers[key] + if !exists { + return nil, fmt.Errorf("no handler registered with key %s", key) + } + + // Get the secret from the secret monitor's cache using the registered handler. + obj, err := m.monitor.GetSecret(ctx, handlerRegistration) + if err != nil { + return nil, err + } + + return obj, nil +} + +// IsRouteRegistered returns true if route is registered, false otherwise +func (m *manager) IsRouteRegistered(namespace, routeName string) bool { + m.handlersLock.RLock() + defer m.handlersLock.RUnlock() + + key := generateKey(namespace, routeName) + _, exists := m.registeredHandlers[key] + return exists +} + +// generateKey creates a unique identifier for a route +func generateKey(namespace, route string) string { + return fmt.Sprintf("%s/%s", namespace, route) +} diff --git a/vendor/github.com/openshift/library-go/pkg/secret/OWNERS b/vendor/github.com/openshift/library-go/pkg/secret/OWNERS new file mode 100644 index 000000000..a13f72be3 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/secret/OWNERS @@ -0,0 +1,9 @@ +reviewers: + - deads2k + - soltysh + - Miciah + - chiragkyal +approvers: + - deads2k + - soltysh + - Miciah diff --git a/vendor/github.com/openshift/library-go/pkg/secret/monitor.go b/vendor/github.com/openshift/library-go/pkg/secret/monitor.go new file mode 100644 index 000000000..c8505acce --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/secret/monitor.go @@ -0,0 +1,139 @@ +package secret + +import ( + "context" + "fmt" + "sync" + + "k8s.io/client-go/tools/cache" + "k8s.io/klog/v2" +) + +// ObjectKey represents the unique identifier for a resource, used to access it in the cache. +type ObjectKey struct { + // Namespace is the namespace in which the resource is located. + Namespace string + // Name denotes metadata.name of a resource being monitored by informer + Name string +} + +// singleItemMonitor monitors a single resource using a SharedInformer. +type singleItemMonitor struct { + key ObjectKey + informer cache.SharedInformer + lock sync.Mutex + stopped bool + stopCh chan struct{} +} + +// NewObjectKey creates a new ObjectKey for the given namespace and name. +func NewObjectKey(namespace, name string) ObjectKey { + return ObjectKey{ + Namespace: namespace, + Name: name, + } +} + +// newSingleItemMonitor creates a new singleItemMonitor for the given key and informer. +func newSingleItemMonitor(key ObjectKey, informer cache.SharedInformer) *singleItemMonitor { + return &singleItemMonitor{ + key: key, + informer: informer, + stopped: true, + stopCh: make(chan struct{}), + } +} + +// HasSynced returns true if the informer's cache has been successfully synced. +func (i *singleItemMonitor) HasSynced() bool { + return i.informer.HasSynced() +} + +// StartInformer starts and runs the informer until the provided context is canceled, +// or StopInformer() is called. +func (i *singleItemMonitor) StartInformer(ctx context.Context) { + i.lock.Lock() + defer i.lock.Unlock() + + if !i.stopped { + klog.Warning("informer is already running") + return + } + + go func() { + select { + case <-ctx.Done(): + klog.V(5).Info("stopping informer due to context cancellation") + if !i.StopInformer() { + klog.Error("failed to stop informer") + } + // this case is required to exit from the goroutine + // after normal StopInformer() call i.e when stopCh is closed. + case <-i.stopCh: + } + }() + + klog.Info("starting informer") + i.stopped = false + + go i.informer.Run(i.stopCh) +} + +// StopInformer stops the informer. +// Retuns false if called twice, or before StartInformer(); true otherwise. +func (i *singleItemMonitor) StopInformer() bool { + i.lock.Lock() + defer i.lock.Unlock() + + if i.stopped { + return false + } + i.stopped = true + close(i.stopCh) // Signal the informer to stop + klog.Info("informer stopped") + return true +} + +// AddEventHandler adds an event handler to the informer and returns +// secretEventHandlerRegistration after populating objectKey and registration. +func (i *singleItemMonitor) AddEventHandler(handler cache.ResourceEventHandler) (SecretEventHandlerRegistration, error) { + i.lock.Lock() + defer i.lock.Unlock() + + if i.stopped { + return nil, fmt.Errorf("cannot add handler %v to already stopped informer", handler) + } + + registration, err := i.informer.AddEventHandler(handler) + if err != nil { + return nil, err + } + + return &secretEventHandlerRegistration{ + ResourceEventHandlerRegistration: registration, + objectKey: i.key, + }, nil +} + +// RemoveEventHandler removes an event handler from the informer. +func (i *singleItemMonitor) RemoveEventHandler(handle SecretEventHandlerRegistration) error { + i.lock.Lock() + defer i.lock.Unlock() + + if handle == nil { + return fmt.Errorf("nil handler registration is provided") + } + + if i.stopped { + return fmt.Errorf("can not remove handler %v from stopped informer", handle.GetHandler()) + } + + return i.informer.RemoveEventHandler(handle.GetHandler()) +} + +// GetItem returns the accumulator being monitored +// by informer, using keyFunc (namespace/name). +func (i *singleItemMonitor) GetItem() (item interface{}, exists bool, err error) { + keyFunc := i.key.Namespace + "/" + i.key.Name + return i.informer.GetStore().GetByKey(keyFunc) +} diff --git a/vendor/github.com/openshift/library-go/pkg/secret/secret_monitor.go b/vendor/github.com/openshift/library-go/pkg/secret/secret_monitor.go new file mode 100644 index 000000000..ddccb0484 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/secret/secret_monitor.go @@ -0,0 +1,217 @@ +package secret + +import ( + "context" + "fmt" + "sync" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/klog/v2" +) + +// SecretEventHandlerRegistration is for registering and unregistering event handlers for secret monitoring. +type SecretEventHandlerRegistration interface { + cache.ResourceEventHandlerRegistration + + GetKey() ObjectKey + GetHandler() cache.ResourceEventHandlerRegistration +} + +// SecretMonitor helps in monitoring and handling a specific secret using singleItemMonitor. +type SecretMonitor interface { + // AddSecretEventHandler adds a secret event handler to the monitor for a specific secret in the given namespace. + // The handler will be notified of events related to the "specified" secret only. + // The returned SecretEventHandlerRegistration can be used to later remove the handler. + AddSecretEventHandler(ctx context.Context, namespace, secretName string, handler cache.ResourceEventHandler) (SecretEventHandlerRegistration, error) + + // RemoveSecretEventHandler removes a previously added secret event handler using the provided registration. + // If the handler is not found or if there is an issue removing it, an error is returned. + RemoveSecretEventHandler(handlerRegistration SecretEventHandlerRegistration) error + + // GetSecret retrieves the secret object from the informer's cache using the provided SecretEventHandlerRegistration. + // This allows accessing the latest state of the secret without making an API call. + GetSecret(ctx context.Context, handlerRegistration SecretEventHandlerRegistration) (*corev1.Secret, error) +} + +// secretEventHandlerRegistration is an implementation of the SecretEventHandlerRegistration. +type secretEventHandlerRegistration struct { + cache.ResourceEventHandlerRegistration + + // objectKey represents the unique identifier for the secret associated with this event handler registration. + // It will be populated during AddEventHandler, and will be used during RemoveEventHandler, GetSecret. + objectKey ObjectKey +} + +func (r *secretEventHandlerRegistration) GetKey() ObjectKey { + return r.objectKey +} + +func (r *secretEventHandlerRegistration) GetHandler() cache.ResourceEventHandlerRegistration { + return r.ResourceEventHandlerRegistration +} + +type monitoredItem struct { + itemMonitor *singleItemMonitor + numHandlers int +} + +// secretMonitor is an implementation of the SecretMonitor +type secretMonitor struct { + kubeClient kubernetes.Interface + lock sync.RWMutex + monitors map[ObjectKey]*monitoredItem +} + +func NewSecretMonitor(kubeClient kubernetes.Interface) SecretMonitor { + return &secretMonitor{ + kubeClient: kubeClient, + monitors: map[ObjectKey]*monitoredItem{}, + } +} + +// AddSecretEventHandler adds a secret event handler to the monitor. +func (s *secretMonitor) AddSecretEventHandler(ctx context.Context, namespace, secretName string, handler cache.ResourceEventHandler) (SecretEventHandlerRegistration, error) { + return s.addSecretEventHandler(ctx, namespace, secretName, handler, s.createSecretInformer(namespace, secretName)) +} + +// createSecretInformer creates a SharedInformer for monitoring a specific secret. +func (s *secretMonitor) createSecretInformer(namespace, name string) cache.SharedInformer { + return cache.NewSharedInformer( + cache.NewListWatchFromClient( + s.kubeClient.CoreV1().RESTClient(), + "secrets", + namespace, + fields.OneTermEqualSelector("metadata.name", name), + ), + &corev1.Secret{}, + 0, + ) +} + +// addSecretEventHandler adds a secret event handler and starts the informer if not already running. +func (s *secretMonitor) addSecretEventHandler(ctx context.Context, namespace, secretName string, handler cache.ResourceEventHandler, secretInformer cache.SharedInformer) (SecretEventHandlerRegistration, error) { + s.lock.Lock() + defer s.lock.Unlock() + + if handler == nil { + return nil, fmt.Errorf("nil handler is provided") + } + + // secret identifier (namespace/secret) + key := NewObjectKey(namespace, secretName) + + // Start secret informer if monitor does not exist. + m, exists := s.monitors[key] + if !exists { + m = &monitoredItem{} + m.itemMonitor = newSingleItemMonitor(key, secretInformer) + m.itemMonitor.StartInformer(ctx) + + // wait for first sync + if !cache.WaitForCacheSync(ctx.Done(), m.itemMonitor.HasSynced) { + return nil, fmt.Errorf("failed waiting for cache sync") + } + + // add item key to monitors map + s.monitors[key] = m + + klog.Info("secret informer started", " item key ", key) + } + + // add the event handler + registration, err := m.itemMonitor.AddEventHandler(handler) + if err != nil { + return nil, err + } + + // Increment numHandlers + m.numHandlers += 1 + + // TODO: this can be too noisy, later we need to use higher verbosity + klog.Info("secret handler added", " item key ", key) + + return registration, nil +} + +// RemoveSecretEventHandler removes a secret event handler and stops the informer if no handlers are left. +// If the handler is not found or if there is an issue removing it, an error is returned. +func (s *secretMonitor) RemoveSecretEventHandler(handlerRegistration SecretEventHandlerRegistration) error { + s.lock.Lock() + defer s.lock.Unlock() + + if handlerRegistration == nil { + return fmt.Errorf("nil secret handler registration is provided") + } + + // Extract the key from the registration to identify the associated monitor. + // populated in AddEventHandler() + key := handlerRegistration.GetKey() + + // check if secret informer already exists for the secret(key) + m, exists := s.monitors[key] + if !exists { + return fmt.Errorf("secret monitor already removed for item key %v", key) + } + + if err := m.itemMonitor.RemoveEventHandler(handlerRegistration); err != nil { + return err + } + // Decrement numHandlers + m.numHandlers -= 1 + klog.Info("secret handler removed", " item key", key) + + // stop informer if there is no handler + if m.numHandlers <= 0 { + if !m.itemMonitor.StopInformer() { + return fmt.Errorf("secret informer already stopped for item key %v", key) + } + // remove the key from map + delete(s.monitors, key) + klog.Info("secret informer stopped", " item key ", key) + } + + return nil +} + +// GetSecret retrieves the secret object from the informer's cache. Error if the secret is not found in the cache. +func (s *secretMonitor) GetSecret(ctx context.Context, handlerRegistration SecretEventHandlerRegistration) (*corev1.Secret, error) { + s.lock.RLock() + defer s.lock.RUnlock() + + if handlerRegistration == nil { + return nil, fmt.Errorf("nil secret handler registration is provided") + } + key := handlerRegistration.GetKey() + secretName := key.Name + + // check if secret informer exists + m, exists := s.monitors[key] + if !exists { + return nil, fmt.Errorf("secret monitor doesn't exist for key %v", key) + } + + // wait for informer store sync, to load secrets + if !cache.WaitForCacheSync(ctx.Done(), handlerRegistration.HasSynced) { + return nil, fmt.Errorf("failed waiting for cache sync") + } + + uncast, exists, err := m.itemMonitor.GetItem() + + if err != nil { + return nil, err + } + if !exists { + return nil, apierrors.NewNotFound(corev1.Resource("secrets"), secretName) + } + + secret, ok := uncast.(*corev1.Secret) + if !ok { + return nil, fmt.Errorf("unexpected type: %T", uncast) + } + + return secret, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4186d1ab7..582bd07a8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -201,13 +201,13 @@ github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg -# github.com/openshift/api v0.0.0-20240202140003-8b34b9854c7f +# github.com/openshift/api v0.0.0-20240424142232-29a704bf5aa2 ## explicit; go 1.21 github.com/openshift/api/project/v1 github.com/openshift/api/route github.com/openshift/api/route/v1 -# github.com/openshift/client-go v0.0.0-20230120202327-72f107311084 -## explicit; go 1.19 +# github.com/openshift/client-go v0.0.0-20240405120947-c67c8325cdd8 +## explicit; go 1.21 github.com/openshift/client-go/project/applyconfigurations/internal github.com/openshift/client-go/project/applyconfigurations/project/v1 github.com/openshift/client-go/project/clientset/versioned @@ -223,10 +223,14 @@ github.com/openshift/client-go/route/clientset/versioned/scheme github.com/openshift/client-go/route/clientset/versioned/typed/route/v1 github.com/openshift/client-go/route/clientset/versioned/typed/route/v1/fake github.com/openshift/client-go/route/listers/route/v1 -# github.com/openshift/library-go v0.0.0-20230120202744-256994f916c4 -## explicit; go 1.19 +# github.com/openshift/library-go v0.0.0-20240426144148-0690e4a4602d +## explicit; go 1.21 +github.com/openshift/library-go/pkg/authorization/authorizationutil github.com/openshift/library-go/pkg/crypto github.com/openshift/library-go/pkg/proc +github.com/openshift/library-go/pkg/route/secretmanager +github.com/openshift/library-go/pkg/route/secretmanager/fake +github.com/openshift/library-go/pkg/secret github.com/openshift/library-go/pkg/serviceability # github.com/pkg/errors v0.9.1 ## explicit