From 82aa21a89418f0bd1412c27165e24e8b719b28b4 Mon Sep 17 00:00:00 2001 From: Walter Medvedeo Date: Fri, 19 Jan 2024 11:18:53 +0100 Subject: [PATCH] kie-kogito-serverless-operator-335: Operator driven service discovery API Phase4 (#338) * kie-kogito-serverless-operator-335: Operator driven service discovery API Phase4 - Add the discovery of Openshift DeploymentConfigs and Routes to the service discovery API * kie-kogito-serverless-operator-335: Operator driven service discovery API Phase4 - Code review suggestions 1 * kie-kogito-serverless-operator-335: Operator driven service discovery API Phase4 - Code review suggestions 2 * kie-kogito-serverless-operator-335: Operator driven service discovery API Phase4 - Augment the service uri query parameters --- ...taflow-operator.clusterserviceversion.yaml | 16 +++ config/rbac/service_discovery_role.yaml | 16 +++ controllers/builder/openshiftbuilder.go | 4 +- controllers/discovery/discovery.go | 6 +- .../discovery/discovery_knative_test.go | 8 +- .../discovery/discovery_openshift_test.go | 123 ++++++++++++++++ controllers/discovery/discovery_test.go | 14 +- controllers/discovery/openshift_catalog.go | 132 ++++++++++++++++++ controllers/discovery/test_utils.go | 5 + controllers/discovery/uri_parser.go | 39 +++++- controllers/discovery/uri_parser_test.go | 58 ++++++++ controllers/openshift/openshift.go | 64 +++++++++ .../common/properties/discovery_test.go | 4 +- operator.yaml | 16 +++ 14 files changed, 482 insertions(+), 23 deletions(-) create mode 100644 controllers/discovery/discovery_openshift_test.go create mode 100644 controllers/discovery/openshift_catalog.go create mode 100644 controllers/openshift/openshift.go diff --git a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml index 2d2949889..94baa4f11 100644 --- a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml +++ b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml @@ -576,6 +576,22 @@ spec: - get - list - watch + - apiGroups: + - apps.openshift.io + resources: + - deploymentconfigs + verbs: + - get + - list + - watch + - apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - get + - list + - watch serviceAccountName: sonataflow-operator-controller-manager deployments: - label: diff --git a/config/rbac/service_discovery_role.yaml b/config/rbac/service_discovery_role.yaml index e7be5d7f9..ea72098fa 100644 --- a/config/rbac/service_discovery_role.yaml +++ b/config/rbac/service_discovery_role.yaml @@ -40,3 +40,19 @@ rules: - get - list - watch + - apiGroups: + - apps.openshift.io + resources: + - deploymentconfigs + verbs: + - get + - list + - watch + - apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - get + - list + - watch diff --git a/controllers/builder/openshiftbuilder.go b/controllers/builder/openshiftbuilder.go index 94d406074..79ada8fe1 100644 --- a/controllers/builder/openshiftbuilder.go +++ b/controllers/builder/openshiftbuilder.go @@ -23,6 +23,8 @@ import ( "context" "strings" + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/openshift" + buildv1 "github.com/openshift/api/build/v1" imgv1 "github.com/openshift/api/image/v1" buildclientv1 "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1" @@ -78,7 +80,7 @@ type openshiftBuilderManager struct { } func newOpenShiftBuilderManager(managerContext buildManagerContext, cliConfig *rest.Config) (BuildManager, error) { - buildClient, err := buildclientv1.NewForConfig(cliConfig) + buildClient, err := openshift.NewOpenShiftBuildClient(cliConfig) if err != nil { return nil, err } diff --git a/controllers/discovery/discovery.go b/controllers/discovery/discovery.go index d5b44255e..6d5dba331 100644 --- a/controllers/discovery/discovery.go +++ b/controllers/discovery/discovery.go @@ -87,10 +87,11 @@ type sonataFlowServiceCatalog struct { } // NewServiceCatalog returns a new ServiceCatalog configured to resolve kubernetes, knative, and openshift resource addresses. -func NewServiceCatalog(cli client.Client, knDiscoveryClient *KnDiscoveryClient) ServiceCatalog { +func NewServiceCatalog(cli client.Client, knDiscoveryClient *KnDiscoveryClient, openShiftDiscoveryClient *OpenShiftDiscoveryClient) ServiceCatalog { return &sonataFlowServiceCatalog{ kubernetesCatalog: newK8SServiceCatalog(cli), knativeCatalog: newKnServiceCatalog(knDiscoveryClient), + openshiftCatalog: newOpenShiftServiceCatalog(openShiftDiscoveryClient), } } @@ -98,6 +99,7 @@ func NewServiceCatalogForConfig(cli client.Client, cfg *rest.Config) ServiceCata return &sonataFlowServiceCatalog{ kubernetesCatalog: newK8SServiceCatalog(cli), knativeCatalog: newKnServiceCatalogForConfig(cfg), + openshiftCatalog: newOpenShiftServiceCatalogForClientAndConfig(cli, cfg), } } @@ -108,7 +110,7 @@ func (c *sonataFlowServiceCatalog) Query(ctx context.Context, uri ResourceUri, o case KnativeScheme: return c.knativeCatalog.Query(ctx, uri, outputFormat) case OpenshiftScheme: - return "", fmt.Errorf("openshift service discovery is not yet implemented") + return c.openshiftCatalog.Query(ctx, uri, outputFormat) default: return "", fmt.Errorf("unknown scheme was provided for service discovery: %s", uri.Scheme) } diff --git a/controllers/discovery/discovery_knative_test.go b/controllers/discovery/discovery_knative_test.go index 9169bbaa8..6fc281f3e 100644 --- a/controllers/discovery/discovery_knative_test.go +++ b/controllers/discovery/discovery_knative_test.go @@ -43,7 +43,7 @@ func Test_QueryKnativeService(t *testing.T) { func Test_QueryKnativeServiceNotFound(t *testing.T) { _, client := fakeservingclient.With(context.TODO()) - ctg := NewServiceCatalog(nil, newKnDiscoveryClient(client.ServingV1(), nil)) + ctg := NewServiceCatalog(nil, newKnDiscoveryClient(client.ServingV1(), nil), nil) doTestQueryWithError(t, ctg, *NewResourceUriBuilder(KnativeScheme). Kind("services"). Group("serving.knative.dev"). @@ -72,7 +72,7 @@ func doTestQueryKnativeService(t *testing.T, expectedUri string) { }, } _, client := fakeservingclient.With(context.TODO(), service) - ctg := NewServiceCatalog(nil, newKnDiscoveryClient(client.ServingV1(), nil)) + ctg := NewServiceCatalog(nil, newKnDiscoveryClient(client.ServingV1(), nil), nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KnativeScheme). Kind("services"). Group("serving.knative.dev"). @@ -87,7 +87,7 @@ func Test_QueryKnativeBroker(t *testing.T) { func Test_QueryKnativeBrokerNotFound(t *testing.T) { _, client := fakeeventingclient.With(context.TODO()) - ctg := NewServiceCatalog(nil, newKnDiscoveryClient(nil, client.EventingV1())) + ctg := NewServiceCatalog(nil, newKnDiscoveryClient(nil, client.EventingV1()), nil) doTestQueryWithError(t, ctg, *NewResourceUriBuilder(KnativeScheme). Kind("brokers"). Group("eventing.knative.dev"). @@ -115,7 +115,7 @@ func doTestQueryKnativeBroker(t *testing.T, expectedUri string) { }, } _, client := fakeeventingclient.With(context.TODO(), broker) - ctg := NewServiceCatalog(nil, newKnDiscoveryClient(nil, client.EventingV1())) + ctg := NewServiceCatalog(nil, newKnDiscoveryClient(nil, client.EventingV1()), nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KnativeScheme). Kind("brokers"). Group("eventing.knative.dev"). diff --git a/controllers/discovery/discovery_openshift_test.go b/controllers/discovery/discovery_openshift_test.go new file mode 100644 index 000000000..ac54da721 --- /dev/null +++ b/controllers/discovery/discovery_openshift_test.go @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package discovery + +import ( + appsv1 "github.com/openshift/api/apps/v1" + routev1 "github.com/openshift/api/route/v1" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + fakeappsclient "github.com/openshift/client-go/apps/clientset/versioned/fake" + fakerouteclient "github.com/openshift/client-go/route/clientset/versioned/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "testing" +) + +func Test_QueryOpenShiftRoute(t *testing.T) { + doTestQueryOpenShiftRoute(t, false, "http://openshiftroutehost1:80") +} + +func Test_QueryOpenShiftRouteWithTLS(t *testing.T) { + doTestQueryOpenShiftRoute(t, true, "https://openshiftroutehost1:443") +} + +func doTestQueryOpenShiftRoute(t *testing.T, tls bool, expectedUri string) { + route := &routev1.Route{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace1, + Name: openShiftRouteName1, + }, + Spec: routev1.RouteSpec{ + Host: openShiftRouteHost1, + }, + Status: routev1.RouteStatus{}, + } + if tls { + route.Spec.TLS = &routev1.TLSConfig{} + } + fakeRoutesClient := fakerouteclient.NewSimpleClientset(route) + ctg := NewServiceCatalog(nil, nil, newOpenShiftDiscoveryClient(nil, fakeRoutesClient.RouteV1(), nil)) + doTestQuery(t, ctg, *NewResourceUriBuilder(OpenshiftScheme). + Kind("routes"). + Group("route.openshift.io"). + Version("v1"). + Namespace(namespace1). + Name(openShiftRouteName1).Build(), "", expectedUri) +} + +func Test_QueryOpenShiftDeploymentConfigWithServiceDNSMode(t *testing.T) { + doTestQueryOpenShiftDeploymentConfig(t, KubernetesDNSAddress, true, "http://service1Name.namespace1.svc:80", "") +} + +func Test_QueryOpenShiftDeploymentConfigWithServiceIPAddressMode(t *testing.T) { + doTestQueryOpenShiftDeploymentConfig(t, KubernetesIPAddress, true, "http://10.1.15.16:80", "") +} + +func Test_QueryOpenShiftDeploymentConfigWithoutServiceDNSMode(t *testing.T) { + doTestQueryOpenShiftDeploymentConfig(t, KubernetesDNSAddress, false, "", "no service was found for the deploymentConfig: openShiftDeploymentConfigName1") +} + +func Test_QueryOpenShiftDeploymentConfigWithoutServiceIPAddressMode(t *testing.T) { + doTestQueryOpenShiftDeploymentConfig(t, KubernetesIPAddress, false, "", "no service was found for the deploymentConfig: openShiftDeploymentConfigName1") +} + +func doTestQueryOpenShiftDeploymentConfig(t *testing.T, outputFormat string, withService bool, expectedUri string, expectedError string) { + selector := map[string]string{ + label1: valueLabel1, + label2: valueLabel2, + } + deploymentConfig := &appsv1.DeploymentConfig{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace1, + Name: openShiftDeploymentConfigName1, + }, + Spec: appsv1.DeploymentConfigSpec{ + Selector: selector, + }, + } + fakeClientBuilder := fake.NewClientBuilder() + if withService { + service := mockServiceWithPorts(namespace1, service1Name, mockServicePort(httpProtocol, tcp, defaultHttpPort)) + service.Spec.Selector = selector + service.Spec.ClusterIP = "10.1.15.16" + service.Spec.Type = corev1.ServiceTypeNodePort + fakeClientBuilder.WithRuntimeObjects(service) + } + cli := fakeClientBuilder.Build() + fakeAppsClient := fakeappsclient.NewSimpleClientset(deploymentConfig) + ctg := NewServiceCatalog(nil, nil, newOpenShiftDiscoveryClient(cli, nil, fakeAppsClient.AppsV1())) + + resourceUri := *NewResourceUriBuilder(OpenshiftScheme). + Kind("deploymentconfigs"). + Group("apps.openshift.io"). + Version("v1"). + Namespace(namespace1). + Name(openShiftDeploymentConfigName1).Build() + + if withService { + doTestQuery(t, ctg, resourceUri, outputFormat, expectedUri) + } else { + doTestQueryWithError(t, ctg, resourceUri, outputFormat, expectedError) + } +} diff --git a/controllers/discovery/discovery_test.go b/controllers/discovery/discovery_test.go index 1c28881b5..12175ca1f 100644 --- a/controllers/discovery/discovery_test.go +++ b/controllers/discovery/discovery_test.go @@ -65,7 +65,7 @@ func doTestQueryKubernetesService(t *testing.T, outputFormat string, expectedUri service.Spec.Type = corev1.ServiceTypeNodePort service.Spec.ClusterIP = "10.1.5.18" cli := fake.NewClientBuilder().WithRuntimeObjects(service).Build() - ctg := NewServiceCatalog(cli, newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(cli, nil, nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme). Kind("services"). Version("v1"). @@ -86,7 +86,7 @@ func doTestQueryKubernetesPod(t *testing.T, outputFormat string, expectedUri str *mockContainerWithPorts("container1Name", mockContainerPort(httpProtocol, tcp, defaultHttpPort))) pod.Status.PodIP = "10.1.12.13" cli := fake.NewClientBuilder().WithRuntimeObjects(pod).Build() - ctg := NewServiceCatalog(cli, newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(cli, nil, nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme). Kind("pods"). Version("v1"). @@ -116,7 +116,7 @@ func doTesQueryKubernetesDeploymentWithService(t *testing.T, outputFormat string service.Spec.Type = corev1.ServiceTypeNodePort cli := fake.NewClientBuilder().WithRuntimeObjects(deployment, service).Build() - ctg := NewServiceCatalog(cli, newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(cli, nil, nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme). Group("apps"). @@ -142,7 +142,7 @@ func doTestQueryKubernetesDeploymentWithoutService(t *testing.T, outputFormat st } deployment := mockDeployment(namespace1, deployment1Name, nil, &selector) - ctg := NewServiceCatalog(fake.NewClientBuilder().WithRuntimeObjects(deployment).Build(), newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(fake.NewClientBuilder().WithRuntimeObjects(deployment).Build(), nil, nil) uri := *NewResourceUriBuilder(KubernetesScheme). Group("apps"). @@ -176,7 +176,7 @@ func doTestQueryKubernetesStatefulSetWithService(t *testing.T, outputFormat stri service.Spec.Type = corev1.ServiceTypeNodePort cli := fake.NewClientBuilder().WithRuntimeObjects(statefulSet, service).Build() - ctg := NewServiceCatalog(cli, newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(cli, nil, nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme). Group("apps"). @@ -202,7 +202,7 @@ func doTestQueryKubernetesStatefulSetWithoutService(t *testing.T, outputFormat s } statefulSet := mockStatefulSet(namespace1, statefulSet1Name, nil, &selector) - ctg := NewServiceCatalog(fake.NewClientBuilder().WithRuntimeObjects(statefulSet).Build(), newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(fake.NewClientBuilder().WithRuntimeObjects(statefulSet).Build(), nil, nil) uri := *NewResourceUriBuilder(KubernetesScheme). Group("apps"). @@ -237,7 +237,7 @@ func doTestQueryKubernetesIngress(t *testing.T, hostName string, ip string, tls ingress.Spec.TLS = []v1.IngressTLS{{}} } cli := fake.NewClientBuilder().WithRuntimeObjects(ingress).Build() - ctg := NewServiceCatalog(cli, newKnDiscoveryClient(nil, nil)) + ctg := NewServiceCatalog(cli, nil, nil) doTestQuery(t, ctg, *NewResourceUriBuilder(KubernetesScheme). Kind("ingresses"). Group("networking.k8s.io"). diff --git a/controllers/discovery/openshift_catalog.go b/controllers/discovery/openshift_catalog.go new file mode 100644 index 000000000..d01a75dca --- /dev/null +++ b/controllers/discovery/openshift_catalog.go @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package discovery + +import ( + "context" + "fmt" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/apache/incubator-kie-kogito-serverless-operator/controllers/openshift" + "github.com/apache/incubator-kie-kogito-serverless-operator/log" + "github.com/apache/incubator-kie-kogito-serverless-operator/utils" + appsv1 "github.com/openshift/client-go/apps/clientset/versioned/typed/apps/v1" + routev1 "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" +) + +const ( + openShiftRoutes = "routes" + openShiftDeploymentConfigs = "deploymentconfigs" +) + +type openShiftServiceCatalog struct { + dc *OpenShiftDiscoveryClient +} + +type OpenShiftDiscoveryClient struct { + Client client.Client + RouteClient routev1.RouteV1Interface + AppsClient appsv1.AppsV1Interface +} + +func newOpenShiftServiceCatalog(discoveryClient *OpenShiftDiscoveryClient) openShiftServiceCatalog { + return openShiftServiceCatalog{ + dc: discoveryClient, + } +} +func newOpenShiftServiceCatalogForClientAndConfig(cli client.Client, cfg *rest.Config) openShiftServiceCatalog { + return openShiftServiceCatalog{ + dc: newOpenShiftDiscoveryClientForClientAndConfig(cli, cfg), + } +} + +func newOpenShiftDiscoveryClientForClientAndConfig(cli client.Client, cfg *rest.Config) *OpenShiftDiscoveryClient { + var routeClient routev1.RouteV1Interface + var appsClient appsv1.AppsV1Interface + var err error + if utils.IsOpenShift() { + if routeClient, err = openshift.GetRouteClient(cfg); err != nil { + klog.V(log.E).ErrorS(err, "Unable to get the openshift route client") + return nil + } + if appsClient, err = openshift.GetAppsClient(cfg); err != nil { + klog.V(log.E).ErrorS(err, "Unable to get the openshift apps client") + return nil + } + return newOpenShiftDiscoveryClient(cli, routeClient, appsClient) + } + return nil +} + +func newOpenShiftDiscoveryClient(cli client.Client, routeClient routev1.RouteV1Interface, appsClient appsv1.AppsV1Interface) *OpenShiftDiscoveryClient { + return &OpenShiftDiscoveryClient{ + Client: cli, + RouteClient: routeClient, + AppsClient: appsClient, + } +} + +func (c openShiftServiceCatalog) Query(ctx context.Context, uri ResourceUri, outputFormat string) (string, error) { + if c.dc == nil { + return "", fmt.Errorf("OpenShiftDiscoveryClient was not provided, maybe current operator is not running in OpenShift") + } + switch uri.GVK.Kind { + case openShiftRoutes: + return c.resolveOpenShiftRouteQuery(ctx, uri) + case openShiftDeploymentConfigs: + return c.resolveOpenShiftDeploymentConfigQuery(ctx, uri, outputFormat) + default: + return "", fmt.Errorf("resolution of openshift kind: %s is not implemented", uri.GVK.Kind) + } +} + +func (c openShiftServiceCatalog) resolveOpenShiftRouteQuery(ctx context.Context, uri ResourceUri) (string, error) { + if route, err := c.dc.RouteClient.Routes(uri.Namespace).Get(ctx, uri.Name, metav1.GetOptions{}); err != nil { + return "", err + } else { + scheme := httpProtocol + port := defaultHttpPort + if route.Spec.TLS != nil { + scheme = httpsProtocol + port = defaultHttpsPort + } + return buildURI(scheme, route.Spec.Host, port), nil + } +} + +func (c openShiftServiceCatalog) resolveOpenShiftDeploymentConfigQuery(ctx context.Context, uri ResourceUri, outputFormat string) (string, error) { + if deploymentConfig, err := c.dc.AppsClient.DeploymentConfigs(uri.Namespace).Get(ctx, uri.Name, metav1.GetOptions{}); err != nil { + return "", err + } else { + if serviceList, err := findServicesBySelectorTarget(ctx, c.dc.Client, uri.Namespace, deploymentConfig.Spec.Selector); err != nil { + return "", err + } else if len(serviceList.Items) == 0 { + return "", fmt.Errorf("no service was found for the deploymentConfig: %s in namespace: %s", uri.Name, uri.Namespace) + } else { + referenceService := selectBestSuitedServiceByCustomLabels(serviceList, uri.GetCustomLabels()) + return resolveServiceUri(referenceService, uri.GetPort(), outputFormat) + } + } +} diff --git a/controllers/discovery/test_utils.go b/controllers/discovery/test_utils.go index 222be9561..c05fea11c 100644 --- a/controllers/discovery/test_utils.go +++ b/controllers/discovery/test_utils.go @@ -58,6 +58,11 @@ const ( knServiceName1 = "knServiceName1" knBrokerName1 = "knBrokerName1" + + openShiftRouteName1 = "openShiftRouteName1" + openShiftRouteHost1 = "openshiftroutehost1" + + openShiftDeploymentConfigName1 = "openShiftDeploymentConfigName1" ) func mockService(namespace string, name string, labels *map[string]string, selectorLabels *map[string]string) *corev1.Service { diff --git a/controllers/discovery/uri_parser.go b/controllers/discovery/uri_parser.go index 78a6375b7..09f9aec0c 100644 --- a/controllers/discovery/uri_parser.go +++ b/controllers/discovery/uri_parser.go @@ -29,10 +29,13 @@ import ( const ( // valid namespace, name, or label name. - dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?" - namespaceAndNamePattern = "^/((" + dns1123LabelFmt + ")+)(/(" + dns1123LabelFmt + ")+)?" - queryStringPattern = "^(\\?((" + dns1123LabelFmt + ")+\\=(" + dns1123LabelFmt + ")+)" + - "(&(" + dns1123LabelFmt + ")+\\=(" + dns1123LabelFmt + ")+)*)?$" + dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?" + queryParamName = "[a-zA-Z0-9][-a-zAz0-9]*" + queryParamValue = "[/a-zA-Z0-9][/-a-zAz0-9]*" + + namespaceAndNamePattern = "^/((" + dns1123LabelFmt + ")+)(/(" + dns1123LabelFmt + ")+)?" + queryStringPattern = "^(\\?((" + queryParamName + ")+\\=(" + queryParamValue + ")+)" + + "(&(" + queryParamName + ")+\\=(" + queryParamValue + ")+)*)?$" kubernetesGroupsPattern = "^(" + kubernetesServices + "|" + kubernetesPods + @@ -63,7 +66,7 @@ func ParseUri(uri string) (*ResourceUri, error) { } else if knativeSimplifiedServiceExpr.MatchString(uri) { return parseKnativeSimplifiedServiceUri(uri) } else if split := openshiftGroupsExpr.Split(uri, -1); len(split) == 2 { - return parseOpenshiftUri(openshiftGroupsExpr.FindString(uri), split[1]) + return parseOpenshiftUri(uri, openshiftGroupsExpr.FindString(uri), split[1]) } return nil, fmt.Errorf("invalid uri: %s, not correspond to any of the available schemes format: %s, %s, %s", uri, KubernetesScheme, KnativeScheme, OpenshiftScheme) } @@ -165,6 +168,18 @@ func parseGVK(schemaGvk string) (*v1.GroupVersionKind, error) { Version: "v1", Kind: "brokers", }, nil + case openshiftRoutes: + return &v1.GroupVersionKind{ + Group: "route.openshift.io", + Version: "v1", + Kind: "routes", + }, nil + case openshiftDeploymentConfigs: + return &v1.GroupVersionKind{ + Group: "apps.openshift.io", + Version: "v1", + Kind: "deploymentconfigs", + }, nil default: return nil, fmt.Errorf("unknown schema and gvk: %s", schemaGvk) } @@ -212,6 +227,16 @@ func parseKnativeSimplifiedServiceUri(uri string) (*ResourceUri, error) { } } -func parseOpenshiftUri(findString string, s string) (*ResourceUri, error) { - return nil, fmt.Errorf("openshit is parsing not yet implemented") +func parseOpenshiftUri(uri string, schemaAndGroup string, after string) (*ResourceUri, error) { + if namespace, name, gvk, queryParams, err := parseNamespaceNameGVKAndQueryParams(uri, schemaAndGroup, after); err != nil { + return nil, err + } else { + return &ResourceUri{ + Scheme: OpenshiftScheme, + GVK: *gvk, + Namespace: namespace, + Name: name, + QueryParams: queryParams, + }, nil + } } diff --git a/controllers/discovery/uri_parser_test.go b/controllers/discovery/uri_parser_test.go index 5d590e175..4b80e3ca3 100644 --- a/controllers/discovery/uri_parser_test.go +++ b/controllers/discovery/uri_parser_test.go @@ -194,6 +194,14 @@ var KnativeServicesTestValues = map[string]*ResourceUri{ WithQueryParam("label-a", "value-a"). WithQueryParam("label-b", "value-b"). WithPort("custom-port-value").Build(), + + "knative:services.v1.serving.knative.dev/my-namespace/my-function?path=/myKnativeFunction": NewResourceUriBuilder(KnativeScheme). + Kind("services"). + Version("v1"). + Group("serving.knative.dev"). + Namespace("my-namespace"). + Name("my-function"). + WithQueryParam("path", "/myKnativeFunction").Build(), } var KnativeBrokersTestValues = map[string]*ResourceUri{ @@ -224,6 +232,44 @@ var KnativeBrokersTestValues = map[string]*ResourceUri{ "knative:brokers.v1.eventing.knative.dev/my-namespace/my-broker/another": nil, } +var OpenshiftRoutesTestValues = map[string]*ResourceUri{ + "openshift:routes.v1.route.openshift.io": nil, + + "openshift:routes.v1.route.openshift.io/my-route": NewResourceUriBuilder(OpenshiftScheme). + Kind("routes"). + Group("route.openshift.io"). + Version("v1"). + Name("my-route"). + Build(), + + "openshift:routes.v1.route.openshift.io/my-namespace/my-route": NewResourceUriBuilder(OpenshiftScheme). + Kind("routes"). + Group("route.openshift.io"). + Version("v1"). + Namespace("my-namespace"). + Name("my-route"). + Build(), +} + +var OpenshiftDeploymentConfigsTestValues = map[string]*ResourceUri{ + "openshift:deploymentconfigs.v1.apps.openshift.io": nil, + + "openshift:deploymentconfigs.v1.apps.openshift.io/my-deployment-config": NewResourceUriBuilder(OpenshiftScheme). + Kind("deploymentconfigs"). + Group("apps.openshift.io"). + Version("v1"). + Name("my-deployment-config"). + Build(), + + "openshift:deploymentconfigs.v1.apps.openshift.io/my-namespace/my-deployment-config": NewResourceUriBuilder(OpenshiftScheme). + Kind("deploymentconfigs"). + Group("apps.openshift.io"). + Version("v1"). + Namespace("my-namespace"). + Name("my-deployment-config"). + Build(), +} + func TestParseKubernetesServicesURI(t *testing.T) { for k, v := range KubernetesServicesTestValues { doTestParseURI(t, k, v) @@ -242,6 +288,18 @@ func TestParseKnativeBrokersURI(t *testing.T) { } } +func TestParseOpenshiftRoutesURI(t *testing.T) { + for k, v := range OpenshiftRoutesTestValues { + doTestParseURI(t, k, v) + } +} + +func TestParseOpenshiftDeploymentConfigsURI(t *testing.T) { + for k, v := range OpenshiftDeploymentConfigsTestValues { + doTestParseURI(t, k, v) + } +} + func doTestParseURI(t *testing.T, url string, expectedUri *ResourceUri) { result, err := ParseUri(url) if expectedUri == nil { diff --git a/controllers/openshift/openshift.go b/controllers/openshift/openshift.go new file mode 100644 index 000000000..57abc481e --- /dev/null +++ b/controllers/openshift/openshift.go @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package openshift + +import ( + appsv1 "github.com/openshift/client-go/apps/clientset/versioned/typed/apps/v1" + buildv1 "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1" + routev1 "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1" + "k8s.io/client-go/rest" +) + +var routeClient routev1.RouteV1Interface +var appsClient appsv1.AppsV1Interface + +func GetRouteClient(cfg *rest.Config) (routev1.RouteV1Interface, error) { + if routeClient == nil { + if osRouteClient, err := NewOpenShiftRouteClient(cfg); err != nil { + return nil, err + } else { + routeClient = osRouteClient + } + } + return routeClient, nil +} + +func GetAppsClient(cfg *rest.Config) (appsv1.AppsV1Interface, error) { + if appsClient == nil { + if osAppsClient, err := NewOpenShiftAppsClientClient(cfg); err != nil { + return nil, err + } else { + appsClient = osAppsClient + } + } + return appsClient, nil +} + +func NewOpenShiftRouteClient(cfg *rest.Config) (*routev1.RouteV1Client, error) { + return routev1.NewForConfig(cfg) +} + +func NewOpenShiftAppsClientClient(cfg *rest.Config) (*appsv1.AppsV1Client, error) { + return appsv1.NewForConfig(cfg) +} + +func NewOpenShiftBuildClient(cfg *rest.Config) (*buildv1.BuildV1Client, error) { + return buildv1.NewForConfig(cfg) +} diff --git a/controllers/profiles/common/properties/discovery_test.go b/controllers/profiles/common/properties/discovery_test.go index 49824f26c..4555c6c38 100644 --- a/controllers/profiles/common/properties/discovery_test.go +++ b/controllers/profiles/common/properties/discovery_test.go @@ -47,11 +47,11 @@ func Test_generateDiscoveryProperties(t *testing.T) { Functions: []model.Function{ { Name: "knServiceInvocation1", - Operation: "knative:services.v1.serving.knative.dev/namespace1/my-kn-service1?path=knative-function1", + Operation: "knative:services.v1.serving.knative.dev/namespace1/my-kn-service1?path=/knative-function1", }, { Name: "knServiceInvocation2", - Operation: "knative:services.v1.serving.knative.dev/my-kn-service3?path=knative-function3", + Operation: "knative:services.v1.serving.knative.dev/my-kn-service3?path=/knative-function3", }, }, } diff --git a/operator.yaml b/operator.yaml index e5b00cfac..068ea81a3 100644 --- a/operator.yaml +++ b/operator.yaml @@ -26334,6 +26334,22 @@ rules: - get - list - watch +- apiGroups: + - apps.openshift.io + resources: + - deploymentconfigs + verbs: + - get + - list + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding