diff --git a/README.md b/README.md index 0f43c76..40e5c9d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ go install github.com/kuadrant/kuadrantctl@latest ## Commands * [Install Kuadrant](doc/install.md) * [Uninstall Kuadrant](doc/uninstall.md) -* [Apply Kuadrant API objects](doc/api-apply.md) * [Generate Istio virtualservice objects](doc/generate-istio-virtualservice.md) * [Generate Istio authenticationpolicy objects](doc/generate-istio-authorizationpolicy.md) * [Generate kuadrat authconfig objects](doc/generate-kuadrant-authconfig.md) diff --git a/cmd/api.go b/cmd/api.go deleted file mode 100644 index d50bf3b..0000000 --- a/cmd/api.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2021 Red Hat, Inc. - -Licensed 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 cmd - -import ( - "github.com/spf13/cobra" -) - -func apiCommand() *cobra.Command { - apiCmd := &cobra.Command{ - Use: "api", - Short: "Commands related to Kuadrant API's", - Long: "The api command provides subcommands to manage kuadrant API manifests", - } - - apiCmd.AddCommand(apiApplyCommand()) - - return apiCmd -} diff --git a/cmd/api_apply.go b/cmd/api_apply.go deleted file mode 100644 index 736c907..0000000 --- a/cmd/api_apply.go +++ /dev/null @@ -1,238 +0,0 @@ -//Copyright 2021 Red Hat, Inc. -// -//Licensed 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 cmd - -import ( - "context" - "errors" - "flag" - "fmt" - "strconv" - - kctlrv1beta1 "github.com/kuadrant/kuadrant-controller/apis/networking/v1beta1" - "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - gatewayapiv1alpha1 "sigs.k8s.io/gateway-api/apis/v1alpha1" - "sigs.k8s.io/yaml" - - "github.com/kuadrant/kuadrantctl/pkg/utils" -) - -var ( - kubeConfig string - apiApplyServiceNamespace string - apiApplyServiceName string - apiApplyScheme string - apiApplyAPIName string - apiApplyTag string - apiApplyPortStr string - apiApplyMatchPath string - apiApplyMatchPathTypeStr string - apiApplyMatchPathType gatewayapiv1alpha1.PathMatchType - apiApplyOAS string - apiApplyToStdout bool -) - -func apiApplyCommand() *cobra.Command { - apiApplyCmd := &cobra.Command{ - Use: "apply", - Short: "Applies a Kuadrant API, installing on a cluster", - Long: "The apply command allows easily to create and update existing *kuadrant API* custom resources", - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - // Required to have controller-runtim config package read the kubeconfig arg - err := flag.CommandLine.Parse([]string{"-kubeconfig", kubeConfig}) - if err != nil { - return err - } - - if apiApplyScheme != "http" && apiApplyScheme != "https" { - return errors.New("not valid scheme. Only ['http', 'https'] allowed") - } - - pathMatchType := gatewayapiv1alpha1.PathMatchType(apiApplyMatchPathTypeStr) - switch pathMatchType { - case gatewayapiv1alpha1.PathMatchExact, gatewayapiv1alpha1.PathMatchPrefix, gatewayapiv1alpha1.PathMatchRegularExpression: - apiApplyMatchPathType = pathMatchType - default: - return fmt.Errorf("not valid match-path-type. Only ['%s', '%s', '%s'] allowed", - gatewayapiv1alpha1.PathMatchExact, gatewayapiv1alpha1.PathMatchPrefix, - gatewayapiv1alpha1.PathMatchRegularExpression) - } - - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - return runApiApply(cmd, args) - }, - } - - // TODO(eastizle): add context flag to switch between kubeconfig contexts - // It would require using config.GetConfigWithContext(context string) (*rest.Config, error) - apiApplyCmd.PersistentFlags().StringVar(&kubeConfig, "kubeconfig", "", "Kubernetes configuration file") - apiApplyCmd.Flags().StringVar(&apiApplyServiceName, "service-name", "", "Service name (required)") - err := apiApplyCmd.MarkFlagRequired("service-name") - if err != nil { - panic(err) - } - apiApplyCmd.Flags().StringVarP(&apiApplyServiceNamespace, "namespace", "n", "", "Service namespace (required)") - err = apiApplyCmd.MarkFlagRequired("namespace") - if err != nil { - panic(err) - } - apiApplyCmd.Flags().StringVar(&apiApplyScheme, "scheme", "http", "Either HTTP or HTTPS specifies how the kuadrant gateway will connect to this API") - apiApplyCmd.Flags().StringVar(&apiApplyAPIName, "api-name", "", "If not set, the name of the API can be matched with the service name") - apiApplyCmd.Flags().StringVar(&apiApplyTag, "tag", "", "A special tag used to distinguish this deployment between several instances of the API") - apiApplyCmd.Flags().StringVar(&apiApplyPortStr, "port", "", "Only required if there are multiple ports in the service. Either the Name of the port or the Number") - apiApplyCmd.Flags().StringVar(&apiApplyMatchPath, "match-path", "/", "Define a single specific path, prefix or regex") - apiApplyCmd.Flags().StringVar(&apiApplyMatchPathTypeStr, "match-path-type", "Prefix", "Specifies how to match against the matchpath value. Accepted values are Exact, Prefix and RegularExpression. Defaults to Prefix") - apiApplyCmd.Flags().StringVar(&apiApplyOAS, "oas", "", "/path/to/file.[json|yaml|yml] OR http[s]://domain/resource/path.[json|yaml|yml] OR -") - apiApplyCmd.Flags().BoolVar(&apiApplyToStdout, "to-stdout", false, "Serialize the kuadrant API object in stdout instead of applying to the cluster") - - return apiApplyCmd -} - -func runApiApply(cmd *cobra.Command, args []string) error { - err := utils.SetupScheme() - if err != nil { - return err - } - - configuration, err := config.GetConfig() - if err != nil { - return err - } - - k8sClient, err := client.New(configuration, client.Options{Scheme: scheme.Scheme}) - if err != nil { - return err - } - - serviceKey := client.ObjectKey{Name: apiApplyServiceName, Namespace: apiApplyServiceNamespace} - - service := &corev1.Service{} - if err := k8sClient.Get(context.TODO(), serviceKey, service); err != nil { - // the service must exist - return err - } - - apiName := service.GetName() - if apiApplyAPIName != "" { - apiName = apiApplyAPIName - } - - if apiApplyTag != "" { - apiName = fmt.Sprintf("%s.%s", apiName, apiApplyTag) - } - - var destinationPort int32 - if apiApplyPortStr != "" { - // check if the port is a number already. - if num, err := strconv.ParseInt(apiApplyPortStr, 10, 32); err == nil { - destinationPort = int32(num) - } else { - // As the port is name, resolv the port from the service - for _, p := range service.Spec.Ports { - if p.Name == apiApplyPortStr { - destinationPort = p.Port - break - } - } - } - } else { - // let's check if the service has only one port, if that's the case, - // default to it. - if len(service.Spec.Ports) != 1 { - return errors.New("multple ports found in the service. Use --port to specify") - } - - destinationPort = service.Spec.Ports[0].Port - } - - // If we reach this point and the Port is still nil, this means bad news - if destinationPort == 0 { - return errors.New("port is missing or invalid") - } - - api := &kctlrv1beta1.API{ - TypeMeta: metav1.TypeMeta{ - Kind: kctlrv1beta1.APIKind, - APIVersion: kctlrv1beta1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: apiName, - Namespace: apiApplyServiceNamespace, - }, - Spec: kctlrv1beta1.APISpec{ - Destination: kctlrv1beta1.Destination{ - Schema: apiApplyScheme, - ServiceReference: apiextensionsv1.ServiceReference{ - Namespace: service.GetNamespace(), - Name: service.GetName(), - Port: &destinationPort, - }, - }, - // Default value for mappings is path match / with Prefix type - Mappings: kctlrv1beta1.APIMappings{ - HTTPPathMatch: &gatewayapiv1alpha1.HTTPPathMatch{ - Type: &apiApplyMatchPathType, Value: &apiApplyMatchPath, - }, - }, - }, - } - - if apiApplyOAS != "" { - dataRaw, err := utils.ReadExternalResource(apiApplyOAS) - if err != nil { - return err - } - - err = utils.ValidateOAS3(dataRaw) - if err != nil { - return err - } - - dataTmp := string(dataRaw) - api.Spec.Mappings = kctlrv1beta1.APIMappings{OAS: &dataTmp} - } - - // Add owner reference. This is not a controller owner reference - err = controllerutil.SetOwnerReference(service, api, scheme.Scheme) - if err != nil { - return err - } - - if apiApplyToStdout { - // In short, this library first converts YAML to JSON using go-yaml - // and then uses json.Marshal and json.Unmarshal to convert to or from the struct - yamlData, err := yaml.Marshal(api) - if err != nil { - return err - } - - fmt.Fprintln(cmd.OutOrStdout(), string(yamlData)) - } else { - err = utils.ReconcileKuadrantAPI(k8sClient, api, utils.KuadrantAPIBasicMutator) - if err != nil { - return err - } - } - - return nil -} diff --git a/cmd/api_apply_test.go b/cmd/api_apply_test.go deleted file mode 100644 index a74b85f..0000000 --- a/cmd/api_apply_test.go +++ /dev/null @@ -1,250 +0,0 @@ -package cmd - -import ( - "bytes" - "context" - "fmt" - "strings" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "github.com/google/uuid" - kctlrv1beta1 "github.com/kuadrant/kuadrant-controller/apis/networking/v1beta1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - gatewayapiv1alpha1 "sigs.k8s.io/gateway-api/apis/v1alpha1" - "sigs.k8s.io/yaml" -) - -var _ = Describe("the api apply command", func() { - var testNamespace string - - BeforeEach(func() { - var generatedTestNamespace = "test-namespace-" + uuid.New().String() - - namespace := &corev1.Namespace{ - TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Namespace"}, - ObjectMeta: metav1.ObjectMeta{Name: generatedTestNamespace}, - } - - // Add any setup steps that needs to be executed before each test - err := testK8sClient.Create(context.Background(), namespace) - Expect(err).ToNot(HaveOccurred()) - - existingNamespace := &corev1.Namespace{} - Eventually(func() bool { - err := testK8sClient.Get(context.Background(), types.NamespacedName{Name: generatedTestNamespace}, existingNamespace) - return err == nil - }, 5*time.Minute, 5*time.Second).Should(BeTrue()) - - testNamespace = existingNamespace.Name - }) - - AfterEach(func() { - desiredTestNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}} - // Add any teardown steps that needs to be executed after each test - err := testK8sClient.Delete(context.Background(), desiredTestNamespace, client.PropagationPolicy(metav1.DeletePropagationForeground)) - - Expect(err).ToNot(HaveOccurred()) - - existingNamespace := &corev1.Namespace{} - Eventually(func() bool { - err := testK8sClient.Get(context.Background(), types.NamespacedName{Name: testNamespace}, existingNamespace) - if err != nil && apierrors.IsNotFound(err) { - return true - } - return false - }, 5*time.Minute, 5*time.Second).Should(BeTrue()) - - }) - - Context("when the match type is invalid", func() { - It("should return not found error", func() { - cmdLine := fmt.Sprintf("api apply --match-path /v1 --match-path-type unknown --service-name someservice -n %s", - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("not valid match-path-type")) - }) - }) - - Context("when the service is not found", func() { - It("should return not found error", func() { - cmdLine := fmt.Sprintf("api apply --service-name someservice -n %s --oas testdata/invalid_oas.yaml", - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("services \"someservice\" not found")) - }) - }) - - Context("when the service exists", func() { - var serviceName string - BeforeEach(func() { - var generatedServiceName = "test-service-" + uuid.New().String() - - service := &corev1.Service{ - TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Service"}, - ObjectMeta: metav1.ObjectMeta{Name: generatedServiceName, Namespace: testNamespace}, - Spec: corev1.ServiceSpec{ - Selector: map[string]string{"key": "value"}, - Ports: []corev1.ServicePort{ - { - Name: "http", - Port: 80, - }, - }, - }, - } - - // Add any setup steps that needs to be executed before each test - err := testK8sClient.Create(context.Background(), service) - Expect(err).ToNot(HaveOccurred()) - - serviceName = service.Name - }) - - Context("when the OAS is invalid", func() { - It("should return validation error", func() { - cmdLine := fmt.Sprintf("api apply --service-name %s -n %s --oas testdata/invalid_oas.yaml", - serviceName, testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("OpenAPI validation error")) - }) - }) - - Context("oas is given", func() { - It("API should contain the oas", func() { - cmdLine := fmt.Sprintf("api apply --service-name %s -n %s --oas testdata/petstore.yaml", - serviceName, - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).ToNot(HaveOccurred()) - - expectedAPIName := serviceName - existingAPI := &kctlrv1beta1.API{} - err = testK8sClient.Get(context.Background(), - types.NamespacedName{Name: expectedAPIName, Namespace: testNamespace}, - existingAPI) - Expect(err).ToNot(HaveOccurred()) - Expect(existingAPI.Spec.Mappings.OAS).NotTo(BeNil()) - }) - }) - - Context("when tag is given", func() { - It("API name should contain the tag", func() { - cmdLine := fmt.Sprintf("api apply --tag production --service-name %s -n %s --oas testdata/petstore.yaml", - serviceName, - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).ToNot(HaveOccurred()) - - expectedAPIName := serviceName + "." + "production" - existingAPI := &kctlrv1beta1.API{} - err = testK8sClient.Get(context.Background(), - types.NamespacedName{Name: expectedAPIName, Namespace: testNamespace}, - existingAPI) - Expect(err).ToNot(HaveOccurred()) - }) - }) - - Context("when port number is given", func() { - It("API destination port has the port", func() { - cmdLine := fmt.Sprintf("api apply --port 78 --service-name %s -n %s --oas testdata/petstore.yaml", - serviceName, - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).ToNot(HaveOccurred()) - expectedAPIName := serviceName - existingAPI := &kctlrv1beta1.API{} - err = testK8sClient.Get(context.Background(), - types.NamespacedName{Name: expectedAPIName, Namespace: testNamespace}, - existingAPI) - Expect(err).ToNot(HaveOccurred()) - Expect(*existingAPI.Spec.Destination.ServiceReference.Port).Should(Equal(int32(78))) - }) - }) - - Context("when port name is given", func() { - It("API destination port has the port", func() { - cmdLine := fmt.Sprintf("api apply --port http --service-name %s -n %s --oas testdata/petstore.yaml", - serviceName, - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).ToNot(HaveOccurred()) - expectedAPIName := serviceName - existingAPI := &kctlrv1beta1.API{} - err = testK8sClient.Get(context.Background(), - types.NamespacedName{Name: expectedAPIName, Namespace: testNamespace}, - existingAPI) - Expect(err).ToNot(HaveOccurred()) - Expect(*existingAPI.Spec.Destination.ServiceReference.Port).Should(Equal(int32(80))) - }) - }) - - Context("when match path is given", func() { - It("API mapping has match path", func() { - cmdLine := fmt.Sprintf("api apply --service-name %s -n %s --match-path /v1 --match-path-type Exact", - serviceName, - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - err := rootCmd.Execute() - Expect(err).ToNot(HaveOccurred()) - expectedAPIName := serviceName - existingAPI := &kctlrv1beta1.API{} - err = testK8sClient.Get(context.Background(), - types.NamespacedName{Name: expectedAPIName, Namespace: testNamespace}, - existingAPI) - Expect(err).ToNot(HaveOccurred()) - Expect(existingAPI.Spec.Mappings.HTTPPathMatch).NotTo(BeNil()) - Expect(existingAPI.Spec.Mappings.HTTPPathMatch.Value).NotTo(BeNil()) - Expect(*existingAPI.Spec.Mappings.HTTPPathMatch.Value).Should(Equal("/v1")) - Expect(existingAPI.Spec.Mappings.HTTPPathMatch.Type).NotTo(BeNil()) - Expect(*existingAPI.Spec.Mappings.HTTPPathMatch.Type).Should(Equal(gatewayapiv1alpha1.PathMatchType("Exact"))) - }) - }) - - Context("with the --to-stdout flg", func() { - It("should write to stdout", func() { - cmdLine := fmt.Sprintf("api apply --to-stdout --service-name %s -n %s --match-path /v1", - serviceName, - testNamespace) - rootCmd := GetRootCmd(strings.Split(cmdLine, " ")) - - var out bytes.Buffer - rootCmd.SetOut(&out) - rootCmd.SetErr(&out) - - err := rootCmd.Execute() - Expect(err).ToNot(HaveOccurred()) - - expectedAPIName := serviceName - existingAPI := &kctlrv1beta1.API{} - err = testK8sClient.Get(context.Background(), - types.NamespacedName{Name: expectedAPIName, Namespace: testNamespace}, - existingAPI) - Expect(err).To(HaveOccurred()) - Expect(apierrors.IsNotFound(err)).To(BeTrue()) - - api := &kctlrv1beta1.API{} - err = yaml.Unmarshal(out.Bytes(), api) - Expect(err).ToNot(HaveOccurred()) - Expect(api.TypeMeta.Kind).Should(Equal("API")) - Expect(api.TypeMeta.APIVersion).Should(Equal(kctlrv1beta1.GroupVersion.String())) - }) - }) - }) -}) diff --git a/cmd/root.go b/cmd/root.go index f2e109c..6fdf883 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -40,7 +40,6 @@ func GetRootCmd(args []string) *cobra.Command { rootCmd.SilenceUsage = true rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output") - rootCmd.AddCommand(apiCommand()) rootCmd.AddCommand(installCommand()) rootCmd.AddCommand(uninstallCommand()) rootCmd.AddCommand(versionCommand()) diff --git a/doc/api-apply.md b/doc/api-apply.md deleted file mode 100644 index 45f9469..0000000 --- a/doc/api-apply.md +++ /dev/null @@ -1,50 +0,0 @@ -## Apply Kuadrant API objects - -The [kuadrant API CRD](https://github.com/Kuadrant/kuadrant-controller/blob/v0.1.1/apis/networking/v1beta1/api_types.go) represent internal APIs bundled in a API product. -The kuadrant API CRD grant API Providers the freedom to map their internal API organization structure to kuadrant. - -The `kuadrantctl api apply` command allows easily to create and update existing *kuadrant API* custom resources. - -A prior condition before the *Kuadrant API custom resource* can be created is that the API Provider -must have a [kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) created for the API being protected. -This command will create (or update) one [kuadrant API](https://github.com/Kuadrant/kuadrant-controller/blob/v0.1.1/apis/networking/v1beta1/api_types.go) -custom resource in the same namespace as the referenced service. - -The `kuadrantctl api apply` command needs to have some info about the API being exposed. It can be either: -* A valid [OpenAPI Specification (OAS) 3.x](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md) -* A match path. It can be a single specific path, prefix or regex - -### OpenAPI specification - -OpenAPI document resource can be provided by one of the following channels: -* Filename in the available path. -* URL format (supported schemes are HTTP and HTTPS). The CLI will try to download from the given address. -* Read from stdin standard input stream. - -### Usage : - -```shell -$ kuadrantctl api apply -h -The apply command allows easily to create and update existing *kuadrant API* custom resources - -Usage: - kuadrantctl api apply [flags] - -Flags: - --api-name string If not set, the name of the API can be matched with the service name - -h, --help help for apply - --kubeconfig string Kubernetes configuration file - --match-path string Define a single specific path, prefix or regex (default "/") - --match-path-type string Specifies how to match against the matchpath value. Accepted values are Exact, Prefix and RegularExpression. Defaults to Prefix (default "Prefix") - -n, --namespace string Service namespace (required) - --oas string /path/to/file.[json|yaml|yml] OR http[s]://domain/resource/path.[json|yaml|yml] OR - - --port string Only required if there are multiple ports in the service. Either the Name of the port or the Number - --scheme string Either HTTP or HTTPS specifies how the kuadrant gateway will connect to this API (default "http") - --service-name string Service name (required) - --tag string A special tag used to distinguish this deployment between several instances of the API - --to-stdout Serialize the kuadrant API object in stdout instead of applying to the cluster - -Global Flags: - --config string config file (default is $HOME/.kuadrantctl.yaml) - -v, --verbose verbose output -``` diff --git a/pkg/utils/mutators.go b/pkg/utils/mutators.go deleted file mode 100644 index 736af1e..0000000 --- a/pkg/utils/mutators.go +++ /dev/null @@ -1,35 +0,0 @@ -package utils - -import ( - "fmt" - "reflect" - - kctlrv1beta1 "github.com/kuadrant/kuadrant-controller/apis/networking/v1beta1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// MutateFn is a function which mutates the existing object into it's desired state. -type MutateFn func(existing, desired client.Object) (bool, error) - -func CreateOnlyMutator(existing, desired client.Object) (bool, error) { - return false, nil -} - -func KuadrantAPIBasicMutator(existingObj, desiredObj client.Object) (bool, error) { - existing, ok := existingObj.(*kctlrv1beta1.API) - if !ok { - return false, fmt.Errorf("%T is not a *kctlrv1beta1.API", existingObj) - } - desired, ok := desiredObj.(*kctlrv1beta1.API) - if !ok { - return false, fmt.Errorf("%T is not a *kctlrv1beta1.API", desiredObj) - } - - updated := false - if !reflect.DeepEqual(existing.Spec, desired.Spec) { - existing.Spec = desired.Spec - updated = true - } - - return updated, nil -} diff --git a/pkg/utils/reconciler.go b/pkg/utils/reconciler.go deleted file mode 100644 index 3241674..0000000 --- a/pkg/utils/reconciler.go +++ /dev/null @@ -1,56 +0,0 @@ -package utils - -import ( - "context" - - kctlrv1beta1 "github.com/kuadrant/kuadrant-controller/apis/networking/v1beta1" - "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -// ReconcileResource attempts to mutate the existing state -// in order to match the desired state. The object's desired state must be reconciled -// with the existing state inside the passed in callback MutateFn. -// -// obj: Object of the same type as the 'desired' object. -// Used to read the resource from the kubernetes cluster. -// Could be zero-valued initialized object. -// desired: Object representing the desired state -// -// It returns an error. -func ReconcileResource(k8sClient client.Client, ctx context.Context, obj, desired client.Object, mutateFn MutateFn) error { - key := client.ObjectKeyFromObject(desired) - - if err := k8sClient.Get(ctx, key, obj); err != nil { - if !errors.IsNotFound(err) { - return err - } - // Not found - - // k8s client could remove TypeMeta after object create - k8sObjKind := desired.DeepCopyObject().GetObjectKind() - err = k8sClient.Create(ctx, desired) - logf.Log.Info("create API object", "GKV", k8sObjKind.GroupVersionKind(), "name", desired.GetName(), "error", err) - return err - } - - update, err := mutateFn(obj, desired) - if err != nil { - return err - } - - if update { - err := k8sClient.Update(ctx, obj) - logf.Log.Info("update API object", "GKV", desired.GetObjectKind().GroupVersionKind(), "name", obj.GetName(), "error", err) - return err - } - - logf.Log.Info("API object is up to date. Nothing to do", "GKV", desired.GetObjectKind().GroupVersionKind(), "name", obj.GetName()) - - return nil -} - -func ReconcileKuadrantAPI(k8sClient client.Client, desired *kctlrv1beta1.API, mutatefn MutateFn) error { - return ReconcileResource(k8sClient, context.TODO(), &kctlrv1beta1.API{}, desired, mutatefn) -}