From c101bba396aa85187f7fcb30608a8f626379b179 Mon Sep 17 00:00:00 2001 From: Le Yao Date: Mon, 23 Nov 2020 06:15:36 +0000 Subject: [PATCH] Application CR implemention Implement Application CR and CR controller. Call Application RESTful API to add rules for Application deployment. Handle missing errors. Signed-off-by: Le Yao Change-Id: I425f431bea20f372dc95cde1439259ad29e93773 --- platform/crd-ctrlr/src/PROJECT | 3 + .../src/api/v1alpha1/sdewanapplication_types.go | 59 ++++++++ .../src/api/v1alpha1/zz_generated.deepcopy.go | 96 +++++++++++++ ...atch.sdewan.akraino.org_sdewanapplications.yaml | 115 +++++++++++++++ .../crd-ctrlr/src/config/crd/kustomization.yaml | 3 + .../patches/cainjection_in_sdewanapplications.yaml | 8 ++ .../crd/patches/webhook_in_sdewanapplications.yaml | 17 +++ platform/crd-ctrlr/src/config/rbac/role.yaml | 20 +++ .../config/rbac/sdewanapplication_editor_role.yaml | 24 ++++ .../config/rbac/sdewanapplication_viewer_role.yaml | 20 +++ .../samples/batch_v1alpha1_sdewanapplication.yaml | 12 ++ .../crd-ctrlr/src/controllers/base_controller.go | 8 +- .../controllers/sdewanapplication_controller.go | 159 +++++++++++++++++++++ platform/crd-ctrlr/src/controllers/suite_test.go | 3 + platform/crd-ctrlr/src/main.go | 8 ++ platform/crd-ctrlr/src/openwrt/app.go | 105 ++++++++++++++ 16 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 platform/crd-ctrlr/src/api/v1alpha1/sdewanapplication_types.go create mode 100644 platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_sdewanapplications.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/patches/cainjection_in_sdewanapplications.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/patches/webhook_in_sdewanapplications.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/sdewanapplication_editor_role.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/sdewanapplication_viewer_role.yaml create mode 100644 platform/crd-ctrlr/src/config/samples/batch_v1alpha1_sdewanapplication.yaml create mode 100644 platform/crd-ctrlr/src/controllers/sdewanapplication_controller.go create mode 100644 platform/crd-ctrlr/src/openwrt/app.go diff --git a/platform/crd-ctrlr/src/PROJECT b/platform/crd-ctrlr/src/PROJECT index a50160b..7c4a411 100644 --- a/platform/crd-ctrlr/src/PROJECT +++ b/platform/crd-ctrlr/src/PROJECT @@ -34,4 +34,7 @@ resources: - group: batch kind: CNFService version: v1alpha1 +- group: batch + kind: SdewanApplication + version: v1alpha1 version: "2" diff --git a/platform/crd-ctrlr/src/api/v1alpha1/sdewanapplication_types.go b/platform/crd-ctrlr/src/api/v1alpha1/sdewanapplication_types.go new file mode 100644 index 0000000..1a70918 --- /dev/null +++ b/platform/crd-ctrlr/src/api/v1alpha1/sdewanapplication_types.go @@ -0,0 +1,59 @@ +/* + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// SdewanApplicationSpec defines the desired state of SdewanApplication +type SdewanApplicationSpec struct { + PodSelector *metav1.LabelSelector `json:"podSelector,omitempty"` + AppNamespace string `json:"appNamespace,omitempty"` +} + +type ApplicationInfo struct { + IpList string +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// SdewanApplication is the Schema for the sdewanapplications API +type SdewanApplication struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SdewanApplicationSpec `json:"spec,omitempty"` + Status SdewanStatus `json:"status,omitempty"` + AppInfo ApplicationInfo `json:"-"` +} + +// +kubebuilder:object:root=true + +// SdewanApplicationList contains a list of SdewanApplication +type SdewanApplicationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SdewanApplication `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SdewanApplication{}, &SdewanApplicationList{}) +} diff --git a/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go b/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go index 83a43ee..ed719e7 100644 --- a/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go +++ b/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go @@ -20,9 +20,25 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationInfo) DeepCopyInto(out *ApplicationInfo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationInfo. +func (in *ApplicationInfo) DeepCopy() *ApplicationInfo { + if in == nil { + return nil + } + out := new(ApplicationInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in BucketPermission) DeepCopyInto(out *BucketPermission) { { @@ -955,6 +971,86 @@ func (in *Mwan3RuleSpec) DeepCopy() *Mwan3RuleSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SdewanApplication) DeepCopyInto(out *SdewanApplication) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + out.AppInfo = in.AppInfo +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SdewanApplication. +func (in *SdewanApplication) DeepCopy() *SdewanApplication { + if in == nil { + return nil + } + out := new(SdewanApplication) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SdewanApplication) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SdewanApplicationList) DeepCopyInto(out *SdewanApplicationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SdewanApplication, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SdewanApplicationList. +func (in *SdewanApplicationList) DeepCopy() *SdewanApplicationList { + if in == nil { + return nil + } + out := new(SdewanApplicationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SdewanApplicationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SdewanApplicationSpec) DeepCopyInto(out *SdewanApplicationSpec) { + *out = *in + if in.PodSelector != nil { + in, out := &in.PodSelector, &out.PodSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SdewanApplicationSpec. +func (in *SdewanApplicationSpec) DeepCopy() *SdewanApplicationSpec { + if in == nil { + return nil + } + out := new(SdewanApplicationSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SdewanStatus) DeepCopyInto(out *SdewanStatus) { *out = *in diff --git a/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_sdewanapplications.yaml b/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_sdewanapplications.yaml new file mode 100644 index 0000000..acea1b0 --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_sdewanapplications.yaml @@ -0,0 +1,115 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: sdewanapplications.batch.sdewan.akraino.org +spec: + group: batch.sdewan.akraino.org + names: + kind: SdewanApplication + listKind: SdewanApplicationList + plural: sdewanapplications + singular: sdewanapplication + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: SdewanApplication is the Schema for the sdewanapplications API + 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: SdewanApplicationSpec defines the desired state of SdewanApplication + properties: + appNamespace: + type: string + podSelector: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + type: object + status: + description: status subsource used for Sdewan rule CRDs + properties: + appliedGeneration: + format: int64 + type: integer + appliedTime: + format: date-time + type: string + message: + type: string + state: + type: string + required: + - state + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/platform/crd-ctrlr/src/config/crd/kustomization.yaml b/platform/crd-ctrlr/src/config/crd/kustomization.yaml index 55e8f20..279ef89 100644 --- a/platform/crd-ctrlr/src/config/crd/kustomization.yaml +++ b/platform/crd-ctrlr/src/config/crd/kustomization.yaml @@ -13,6 +13,7 @@ resources: - bases/batch.sdewan.akraino.org_ipsechosts.yaml - bases/batch.sdewan.akraino.org_ipsecsites.yaml - bases/batch.sdewan.akraino.org_cnfservices.yaml +- bases/batch.sdewan.akraino.org_sdewanapplications.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -29,6 +30,7 @@ patchesStrategicMerge: #- patches/webhook_in_ipsechosts.yaml #- patches/webhook_in_ipsecsites.yaml #- patches/webhook_in_cnfservices.yaml +#- patches/webhook_in_sdewanapplications.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. @@ -44,6 +46,7 @@ patchesStrategicMerge: #- patches/cainjection_in_ipsechosts.yaml #- patches/cainjection_in_ipsecsites.yaml #- patches/cainjection_in_cnfservices.yaml +#- patches/cainjection_in_sdewanapplications.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_sdewanapplications.yaml b/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_sdewanapplications.yaml new file mode 100644 index 0000000..1107664 --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_sdewanapplications.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: sdewanapplications.batch.sdewan.akraino.org diff --git a/platform/crd-ctrlr/src/config/crd/patches/webhook_in_sdewanapplications.yaml b/platform/crd-ctrlr/src/config/crd/patches/webhook_in_sdewanapplications.yaml new file mode 100644 index 0000000..898c876 --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/patches/webhook_in_sdewanapplications.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: sdewanapplications.batch.sdewan.akraino.org +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/platform/crd-ctrlr/src/config/rbac/role.yaml b/platform/crd-ctrlr/src/config/rbac/role.yaml index 81499f9..c2ce53f 100644 --- a/platform/crd-ctrlr/src/config/rbac/role.yaml +++ b/platform/crd-ctrlr/src/config/rbac/role.yaml @@ -242,6 +242,26 @@ rules: - get - patch - update +- apiGroups: + - batch.sdewan.akraino.org + resources: + - sdewanapplications + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch.sdewan.akraino.org + resources: + - sdewanapplications/status + verbs: + - get + - patch + - update - apiGroups: - rbac.authorization.k8s.io resources: diff --git a/platform/crd-ctrlr/src/config/rbac/sdewanapplication_editor_role.yaml b/platform/crd-ctrlr/src/config/rbac/sdewanapplication_editor_role.yaml new file mode 100644 index 0000000..c4ff836 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/sdewanapplication_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit sdewanapplications. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sdewanapplication-editor-role +rules: +- apiGroups: + - batch.sdewan.akraino.org + resources: + - sdewanapplications + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch.sdewan.akraino.org + resources: + - sdewanapplications/status + verbs: + - get diff --git a/platform/crd-ctrlr/src/config/rbac/sdewanapplication_viewer_role.yaml b/platform/crd-ctrlr/src/config/rbac/sdewanapplication_viewer_role.yaml new file mode 100644 index 0000000..984f543 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/sdewanapplication_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view sdewanapplications. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: sdewanapplication-viewer-role +rules: +- apiGroups: + - batch.sdewan.akraino.org + resources: + - sdewanapplications + verbs: + - get + - list + - watch +- apiGroups: + - batch.sdewan.akraino.org + resources: + - sdewanapplications/status + verbs: + - get diff --git a/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_sdewanapplication.yaml b/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_sdewanapplication.yaml new file mode 100644 index 0000000..4d2d975 --- /dev/null +++ b/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_sdewanapplication.yaml @@ -0,0 +1,12 @@ +apiVersion: batch.sdewan.akraino.org/v1alpha1 +kind: SdewanApplication +metadata: + name: sdewanapplication-sample + labels: + sdewanPurpose: cnf1 +spec: + appNamespace: default + podSelector: + matchLabels: + app: myapp + #key: value diff --git a/platform/crd-ctrlr/src/controllers/base_controller.go b/platform/crd-ctrlr/src/controllers/base_controller.go index fdd36a0..efeaa04 100644 --- a/platform/crd-ctrlr/src/controllers/base_controller.go +++ b/platform/crd-ctrlr/src/controllers/base_controller.go @@ -244,6 +244,11 @@ func ProcessReconcile(r client.Client, logger logr.Logger, req ctrl.Request, han // No instance return ctrl.Result{}, nil } + + err1, ok := err.(*AppCRError) + if ok && err1.Code == 404 { + return ctrl.Result{}, nil + } // Error reading the object - requeue the request. return ctrl.Result{RequeueAfter: during}, nil } @@ -316,7 +321,8 @@ func ProcessReconcile(r client.Client, logger logr.Logger, req ctrl.Request, han _, err := cnf.DeleteObject(handler, instance) if err != nil { - if err.(*openwrt.OpenwrtError).Code != 404 { + err2, ok := err.(*openwrt.OpenwrtError) + if !ok || err2.Code != 404 { log.Error(err, "Failed to delete "+handler.GetType()) setStatus(instance, batchv1alpha1.SdewanStatus{State: batchv1alpha1.Deleting, Message: err.Error()}) err = r.Status().Update(ctx, instance) diff --git a/platform/crd-ctrlr/src/controllers/sdewanapplication_controller.go b/platform/crd-ctrlr/src/controllers/sdewanapplication_controller.go new file mode 100644 index 0000000..dfd8eea --- /dev/null +++ b/platform/crd-ctrlr/src/controllers/sdewanapplication_controller.go @@ -0,0 +1,159 @@ +/* + +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 controllers + +import ( + "context" + "fmt" + "reflect" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/source" + + batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1" + "sdewan.akraino.org/sdewan/openwrt" +) + +var sdewanApplicationHandler = new(SdewanApplicationHandler) + +type SdewanApplicationHandler struct { +} + +type AppCRError struct { + Code int + Message string +} + +func (e AppCRError) Error() string { + return fmt.Sprintf("Error Code: %d, Error Message: %s", e.Code, e.Message) +} + +func (m *SdewanApplicationHandler) GetType() string { + return "sdewanApplication" +} + +func (m *SdewanApplicationHandler) GetName(instance runtime.Object) string { + app := instance.(*batchv1alpha1.SdewanApplication) + return app.Name +} + +func (m *SdewanApplicationHandler) GetFinalizer() string { + return "rule.finalizers.sdewan.akraino.org" +} + +func (m *SdewanApplicationHandler) GetInstance(r client.Client, ctx context.Context, req ctrl.Request) (runtime.Object, error) { + instance := &batchv1alpha1.SdewanApplication{} + err := r.Get(ctx, req.NamespacedName, instance) + if err == nil { + ps := instance.Spec.PodSelector.MatchLabels + ns := instance.Spec.AppNamespace + podList := &corev1.PodList{} + r.List(ctx, podList, client.MatchingLabels(ps), client.InNamespace(ns)) + ips := "" + for _, item := range podList.Items { + if ips == "" { + ips = item.Status.PodIP + } else { + ips = ips + "," + item.Status.PodIP + } + } + instance.AppInfo.IpList = ips + } + + if instance.AppInfo.IpList == "" { + return instance, &AppCRError{Code: 404, Message: "Application not found"} + } + + return instance, err +} + +func (m *SdewanApplicationHandler) Convert(instance runtime.Object, deployment appsv1.Deployment) (openwrt.IOpenWrtObject, error) { + app := instance.(*batchv1alpha1.SdewanApplication) + openwrtapp := openwrt.SdewanApp{ + Name: app.Name, + IpList: app.AppInfo.IpList, + } + return &openwrtapp, nil +} + +func (m *SdewanApplicationHandler) IsEqual(instance1 openwrt.IOpenWrtObject, instance2 openwrt.IOpenWrtObject) bool { + app1 := instance1.(*openwrt.SdewanApp) + app2 := instance2.(*openwrt.SdewanApp) + return reflect.DeepEqual(*app1, *app2) +} + +func (m *SdewanApplicationHandler) GetObject(clientInfo *openwrt.OpenwrtClientInfo, name string) (openwrt.IOpenWrtObject, error) { + openwrtClient := openwrt.GetOpenwrtClient(*clientInfo) + app := openwrt.AppClient{OpenwrtClient: openwrtClient} + ret, err := app.GetApp(name) + return ret, err +} + +func (m *SdewanApplicationHandler) CreateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) { + openwrtClient := openwrt.GetOpenwrtClient(*clientInfo) + app := openwrt.AppClient{OpenwrtClient: openwrtClient} + application := instance.(*openwrt.SdewanApp) + return app.CreateApp(*application) +} + +func (m *SdewanApplicationHandler) UpdateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) { + openwrtClient := openwrt.GetOpenwrtClient(*clientInfo) + app := openwrt.AppClient{OpenwrtClient: openwrtClient} + application := instance.(*openwrt.SdewanApp) + return app.UpdateApp(*application) +} + +func (m *SdewanApplicationHandler) DeleteObject(clientInfo *openwrt.OpenwrtClientInfo, name string) error { + openwrtClient := openwrt.GetOpenwrtClient(*clientInfo) + app := openwrt.AppClient{OpenwrtClient: openwrtClient} + return app.DeleteApp(name) +} + +func (m *SdewanApplicationHandler) Restart(clientInfo *openwrt.OpenwrtClientInfo) (bool, error) { + return true, nil +} + +// SdewanApplicationReconciler reconciles a SdewanApplication object +type SdewanApplicationReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=sdewanapplications,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=sdewanapplications/status,verbs=get;update;patch + +func (r *SdewanApplicationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + return ProcessReconcile(r, r.Log, req, sdewanApplicationHandler) +} + +func (r *SdewanApplicationReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&batchv1alpha1.SdewanApplication{}). + Watches( + &source.Kind{Type: &corev1.Service{}}, + &handler.EnqueueRequestsFromMapFunc{ + ToRequests: handler.ToRequestsFunc(GetServiceToRequestsFunc(r)), + }, + IPFilter). + Complete(r) +} diff --git a/platform/crd-ctrlr/src/controllers/suite_test.go b/platform/crd-ctrlr/src/controllers/suite_test.go index e096a3c..bf86e57 100644 --- a/platform/crd-ctrlr/src/controllers/suite_test.go +++ b/platform/crd-ctrlr/src/controllers/suite_test.go @@ -92,6 +92,9 @@ var _ = BeforeSuite(func(done Done) { err = batchv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = batchv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) diff --git a/platform/crd-ctrlr/src/main.go b/platform/crd-ctrlr/src/main.go index 0c16027..c50c83d 100644 --- a/platform/crd-ctrlr/src/main.go +++ b/platform/crd-ctrlr/src/main.go @@ -217,6 +217,14 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "CNFService") os.Exit(1) } + if err = (&controllers.SdewanApplicationReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("SdewanApplication"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "SdewanApplication") + os.Exit(1) + } // +kubebuilder:scaffold:builder setupLog.Info("starting manager") diff --git a/platform/crd-ctrlr/src/openwrt/app.go b/platform/crd-ctrlr/src/openwrt/app.go new file mode 100644 index 0000000..d465d05 --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/app.go @@ -0,0 +1,105 @@ +package openwrt + +import ( + "encoding/json" +) + +const ( + appBaseURL = "sdewan/application/v1/" +) + +type AppClient struct { + OpenwrtClient *openwrtClient +} + +// App Info +type SdewanApp struct { + Name string `json:"name"` + IpList string `json:"iplist"` +} + +type SdewanApps struct { + Apps []SdewanApp `json:"apps"` +} + +func (o *SdewanApp) GetName() string { + return o.Name +} + +// App APIs +// get apps +func (m *AppClient) GetApps() (*SdewanApps, error) { + response, err := m.OpenwrtClient.Get(appBaseURL + "applications") + if err != nil { + return nil, err + } + + var sdewanApps SdewanApps + err2 := json.Unmarshal([]byte(response), &sdewanApps) + if err2 != nil { + return nil, err2 + } + + return &sdewanApps, nil +} + +// get app +func (m *AppClient) GetApp(app_name string) (*SdewanApp, error) { + response, err := m.OpenwrtClient.Get(appBaseURL + "applications/" + app_name) + if err != nil { + return nil, err + } + + var sdewanApp SdewanApp + err2 := json.Unmarshal([]byte(response), &sdewanApp) + if err2 != nil { + return nil, err2 + } + + return &sdewanApp, nil +} + +// create app +func (m *AppClient) CreateApp(app SdewanApp) (*SdewanApp, error) { + app_obj, _ := json.Marshal(app) + response, err := m.OpenwrtClient.Post(appBaseURL+"applications/", string(app_obj)) + if err != nil { + return nil, err + } + + var sdewanApp SdewanApp + err2 := json.Unmarshal([]byte(response), &sdewanApp) + if err2 != nil { + return nil, err2 + } + + return &sdewanApp, nil +} + +// delete app +func (m *AppClient) DeleteApp(app_name string) error { + _, err := m.OpenwrtClient.Delete(appBaseURL + "applications/" + app_name) + if err != nil { + return err + } + + return nil +} + +// update app +func (m *AppClient) UpdateApp(app SdewanApp) (*SdewanApp, error) { + app_obj, _ := json.Marshal(app) + app_name := app.Name + response, err := m.OpenwrtClient.Put(appBaseURL+"applications/"+app_name, string(app_obj)) + if err != nil { + return nil, err + } + + var sdewanApp SdewanApp + err2 := json.Unmarshal([]byte(response), &sdewanApp) + if err2 != nil { + return nil, err2 + } + + return &sdewanApp, nil +} -- 2.16.6