From 94e1f2607252b93e9491fbc753dee9a892b36977 Mon Sep 17 00:00:00 2001 From: chengli3 Date: Wed, 22 Apr 2020 02:50:41 +0000 Subject: [PATCH] Runable framework with Mwan3Policy implemented We are going to implement many rule CRDs/controllers. They are mwan3policy, mwan3rule, firewallzone, firewallrule, etc. This patch is the first one which constructs the sdewan controller framework with Mwan3Policy implemented. The design is located on the wiki page[1]. The develop framework is described in the README.md under platform/crd-ctrlr. [1] https://wiki.akraino.org/display/AK/Sdewan+config+Agent Signed-off-by: chengli3 Change-Id: I7cf3b34ece8756c80969c99d9ab8c7383c43ea53 --- platform/crd-ctrlr/README.md | 102 ++++- platform/crd-ctrlr/diagrams/sdewan_dev.drawio | 1 + platform/crd-ctrlr/diagrams/sdewan_dev.png | Bin 0 -> 198388 bytes platform/crd-ctrlr/examples/README.md | 8 + .../crd-ctrlr/examples/attach-network-ovn.yaml | 11 + .../examples/cnf-deployment-older-than-1.16.yaml | 143 +++++++ platform/crd-ctrlr/examples/cnf-deployment.yaml | 145 +++++++ platform/crd-ctrlr/examples/ovn-net1.yaml | 10 + platform/crd-ctrlr/examples/ovn-net2.yml | 10 + platform/crd-ctrlr/examples/ovn-provnet.yaml | 19 + platform/crd-ctrlr/examples/sdewan-controller.yaml | 279 +++++++++++++ platform/crd-ctrlr/src/Dockerfile | 29 ++ platform/crd-ctrlr/src/Makefile | 85 ++++ platform/crd-ctrlr/src/PROJECT | 7 + platform/crd-ctrlr/src/README.md | 65 --- .../crd-ctrlr/src/api/v1alpha1/common_types.go | 29 ++ .../src/api/v1alpha1/groupversion_info.go | 35 ++ .../src/api/v1alpha1/mwan3policy_types.go | 61 +++ .../src/api/v1alpha1/zz_generated.deepcopy.go | 137 +++++++ platform/crd-ctrlr/src/cnfprovider/cnfprovider.go | 12 + platform/crd-ctrlr/src/cnfprovider/wrtprovider.go | 174 ++++++++ .../src/config/certmanager/certificate.yaml | 25 ++ .../src/config/certmanager/kustomization.yaml | 5 + .../src/config/certmanager/kustomizeconfig.yaml | 16 + .../batch.sdewan.akraino.org_mwan3policies.yaml | 89 +++++ .../crd-ctrlr/src/config/crd/kustomization.yaml | 21 + .../crd-ctrlr/src/config/crd/kustomizeconfig.yaml | 17 + .../crd/patches/cainjection_in_mwan3policies.yaml | 8 + .../crd/patches/webhook_in_mwan3policies.yaml | 17 + .../src/config/default/kustomization.yaml | 74 ++++ .../config/default/manager_auth_proxy_patch.yaml | 25 ++ .../src/config/default/manager_webhook_patch.yaml | 23 ++ .../config/default/webhookcainjection_patch.yaml | 15 + .../src/config/manager/kustomization.yaml | 8 + platform/crd-ctrlr/src/config/manager/manager.yaml | 39 ++ .../src/config/prometheus/kustomization.yaml | 2 + .../crd-ctrlr/src/config/prometheus/monitor.yaml | 15 + .../crd-ctrlr/src/config/rbac/auth_proxy_role.yaml | 13 + .../src/config/rbac/auth_proxy_role_binding.yaml | 12 + .../src/config/rbac/auth_proxy_service.yaml | 14 + .../crd-ctrlr/src/config/rbac/kustomization.yaml | 11 + .../src/config/rbac/leader_election_role.yaml | 32 ++ .../config/rbac/leader_election_role_binding.yaml | 12 + .../src/config/rbac/mwan3policy_editor_role.yaml | 26 ++ .../src/config/rbac/mwan3policy_viewer_role.yaml | 20 + platform/crd-ctrlr/src/config/rbac/role.yaml | 36 ++ .../crd-ctrlr/src/config/rbac/role_binding.yaml | 12 + .../config/samples/batch_v1alpha1_mwan3policy.yaml | 15 + .../src/config/webhook/kustomization.yaml | 6 + .../src/config/webhook/kustomizeconfig.yaml | 25 ++ .../crd-ctrlr/src/config/webhook/manifests.yaml | 0 platform/crd-ctrlr/src/config/webhook/service.yaml | 12 + .../src/controllers/mwan3policy_controller.go | 149 +++++++ platform/crd-ctrlr/src/controllers/suite_test.go | 79 ++++ platform/crd-ctrlr/src/go.mod | 13 + platform/crd-ctrlr/src/go.sum | 381 ++++++++++++++++++ platform/crd-ctrlr/src/hack/boilerplate.go.txt | 14 + platform/crd-ctrlr/src/main.go | 82 ++++ platform/crd-ctrlr/src/openwrt/firewall.go | 437 +++++++++++++++++++++ platform/crd-ctrlr/src/openwrt/ipsec.go | 233 +++++++++++ platform/crd-ctrlr/src/openwrt/mwan3.go | 242 ++++++++++++ platform/crd-ctrlr/src/openwrt/openwrtclient.go | 166 ++++++++ platform/crd-ctrlr/src/openwrt/service.go | 54 +++ platform/crd-ctrlr/src/openwrt/utils.go | 22 ++ 64 files changed, 3813 insertions(+), 66 deletions(-) create mode 100644 platform/crd-ctrlr/diagrams/sdewan_dev.drawio create mode 100644 platform/crd-ctrlr/diagrams/sdewan_dev.png create mode 100644 platform/crd-ctrlr/examples/attach-network-ovn.yaml create mode 100644 platform/crd-ctrlr/examples/cnf-deployment-older-than-1.16.yaml create mode 100644 platform/crd-ctrlr/examples/cnf-deployment.yaml create mode 100644 platform/crd-ctrlr/examples/ovn-net1.yaml create mode 100644 platform/crd-ctrlr/examples/ovn-net2.yml create mode 100644 platform/crd-ctrlr/examples/ovn-provnet.yaml create mode 100644 platform/crd-ctrlr/examples/sdewan-controller.yaml create mode 100644 platform/crd-ctrlr/src/Dockerfile create mode 100644 platform/crd-ctrlr/src/Makefile create mode 100644 platform/crd-ctrlr/src/PROJECT delete mode 100644 platform/crd-ctrlr/src/README.md create mode 100644 platform/crd-ctrlr/src/api/v1alpha1/common_types.go create mode 100644 platform/crd-ctrlr/src/api/v1alpha1/groupversion_info.go create mode 100644 platform/crd-ctrlr/src/api/v1alpha1/mwan3policy_types.go create mode 100644 platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go create mode 100644 platform/crd-ctrlr/src/cnfprovider/cnfprovider.go create mode 100644 platform/crd-ctrlr/src/cnfprovider/wrtprovider.go create mode 100644 platform/crd-ctrlr/src/config/certmanager/certificate.yaml create mode 100644 platform/crd-ctrlr/src/config/certmanager/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/certmanager/kustomizeconfig.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_mwan3policies.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/kustomizeconfig.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/patches/cainjection_in_mwan3policies.yaml create mode 100644 platform/crd-ctrlr/src/config/crd/patches/webhook_in_mwan3policies.yaml create mode 100644 platform/crd-ctrlr/src/config/default/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/default/manager_auth_proxy_patch.yaml create mode 100644 platform/crd-ctrlr/src/config/default/manager_webhook_patch.yaml create mode 100644 platform/crd-ctrlr/src/config/default/webhookcainjection_patch.yaml create mode 100644 platform/crd-ctrlr/src/config/manager/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/manager/manager.yaml create mode 100644 platform/crd-ctrlr/src/config/prometheus/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/prometheus/monitor.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/auth_proxy_role.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/auth_proxy_role_binding.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/auth_proxy_service.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/leader_election_role.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/leader_election_role_binding.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/mwan3policy_editor_role.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/mwan3policy_viewer_role.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/role.yaml create mode 100644 platform/crd-ctrlr/src/config/rbac/role_binding.yaml create mode 100644 platform/crd-ctrlr/src/config/samples/batch_v1alpha1_mwan3policy.yaml create mode 100644 platform/crd-ctrlr/src/config/webhook/kustomization.yaml create mode 100644 platform/crd-ctrlr/src/config/webhook/kustomizeconfig.yaml create mode 100644 platform/crd-ctrlr/src/config/webhook/manifests.yaml create mode 100644 platform/crd-ctrlr/src/config/webhook/service.yaml create mode 100644 platform/crd-ctrlr/src/controllers/mwan3policy_controller.go create mode 100644 platform/crd-ctrlr/src/controllers/suite_test.go create mode 100644 platform/crd-ctrlr/src/go.mod create mode 100644 platform/crd-ctrlr/src/go.sum create mode 100644 platform/crd-ctrlr/src/hack/boilerplate.go.txt create mode 100644 platform/crd-ctrlr/src/main.go create mode 100644 platform/crd-ctrlr/src/openwrt/firewall.go create mode 100644 platform/crd-ctrlr/src/openwrt/ipsec.go create mode 100644 platform/crd-ctrlr/src/openwrt/mwan3.go create mode 100644 platform/crd-ctrlr/src/openwrt/openwrtclient.go create mode 100644 platform/crd-ctrlr/src/openwrt/service.go create mode 100644 platform/crd-ctrlr/src/openwrt/utils.go diff --git a/platform/crd-ctrlr/README.md b/platform/crd-ctrlr/README.md index 47c5d14..eef9ed1 100644 --- a/platform/crd-ctrlr/README.md +++ b/platform/crd-ctrlr/README.md @@ -1 +1,101 @@ -# sdewan-controller +# Sdewan operator + +The sdewan operator is developed under kubebuilder framework + +## Deployment Guide + +The API admission webhook depends on cert-manager so we need to install cert-manager first. + +We have the image built and published at `integratedcloudnative/sdewan-controller:dev`. The openwrt +docker image we used for test is at `integratedcloudnative/openwrt:dev`. To use some other images, +we need to make changes in deployment yaml file. + +After clone the repo, please change into directory `platform/crd-ctrlr`. +We are going to run command from this directory in the deployment guide. + +The installation steps for Sdewan operator: +1. kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.11.0/cert-manager.yaml --validate=false +2. kubectl apply -f examples/sdewan-controller.yaml + +Sample deployment of CNF: +1. Setup ovn networks + ``` + kubectl apply -f examples/attach-network-ovn.yaml + kubectl apply -f examples/ovn-net1.yaml + kubectl apply -f examples/ovn-net2.yml + ``` +2. Launch CNF deployment. **NOTE:** CNF deployment is supposed to bind to a Node. + For the sample cnf yaml, we bind it to master node. You can bind to other node by modifying the `nodeSelector`. + ``` + kubectl apply -f examples/cnf-deployment.yaml # for kubernetes older than 1.16, please use cnf-deployment-older-than-1.16.yaml + ``` +3. Create rule for the CNF + ``` + kubectl apply -f src/config/samples/batch_v1alpha1_mwan3policy.yaml + ``` +4. Verify that the policy is applied with the CNF by checking the last lines of mwan3 file. It should contains line `config policy 'balance1'` + ``` + kubectl get pod |grep cnf1 + kubectl exec cnf1-6d759f9b4b-fbqrr -- cat /etc/config/mwan3 + # or you can merge these two commands: kubectl exec `kubectl get pod |grep cnf1 |head -n1 | awk '{print $1}'` -- cat /etc/config/mwan3 + ``` + + +## Developer Guide + +Project initialization(mostly, developers should not execute this step) +``` +go mod init sdewan.akraino.org/sdewan +kubebuilder init --domain sdewan.akraino.org +``` + +To create new CRD and controller +``` +kubebuilder create api --group batch --version v1alpha1 --kind Mwan3Policy +``` + +To run local controller without webhook(For test/debug purpose) +``` +make install +make run ENABLE_WEBHOOKS=false +``` + +To build controller docker image +``` +make docker-build IMG="integratedcloudnative/sdewan-controller:dev" +``` + +To generate yaml file for controller deployment +``` +make gen-yaml IMG="integratedcloudnative/sdewan-controller:dev" +``` + +### Controller Implementation + +![sdewan_dev](diagrams/sdewan_dev.png) + +- One CRD one controller +- Controller watches itself CR and the Deployment(ready status only) +- Reconcile calls WrtProvider to add/update/delete rules for CNF +- CnfProvider interfaces defines the function CNF function calls. WrtProvider is one implementation of CnfProvider +- For the users, CNF rules are CRs. But for openwrt, the rules are openwrt rule entities. We can pass the CRs to OpenWRT API. Instead, we need to convert the CRs to OpenWRT entities. +- Finalizer should be added to CR only when AddUpdate call succeed. Likewise, finalizer should be removed from CR only when Delete call succeed. +- **As we have many CRDs, so there could be many duplicate code. For example, convertCrd, AddUpdateXX, and even reconcile logic. So we need to extract the similar logic into functions to reduce the duplicatioin.** + +### What we have implemented + +- CNF image built from HuiFeng's script. I have uploaded the image at `integratedcloudnative/openwrt:dev` +- The CNF sample deployment yaml file under sample directory (together with configmap and ovn network yaml files) +- A runable framework with Mwan3Policy CRD and controller implemented. It means we can run the controller and add/update/delete mwan3policy rules. + +### What we don't have yet + +- Add a watch for deployment, so that the controller can get the CNF ready status change. [predicate feature](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/predicate#example-Funcs) should be used to filter no-status event. +- Implemente the remain CRDs/controllers. As all the controller logics are almost the same, some workload will be the extracting of the similar logic and make them functions. +- Add raw webhook to implemente the label based permission system +- Add validation webhook to validate CR + +## References + +- https://book.kubebuilder.io/ +- https://openwrt.org/ diff --git a/platform/crd-ctrlr/diagrams/sdewan_dev.drawio b/platform/crd-ctrlr/diagrams/sdewan_dev.drawio new file mode 100644 index 0000000..519c8d7 --- /dev/null +++ b/platform/crd-ctrlr/diagrams/sdewan_dev.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/platform/crd-ctrlr/diagrams/sdewan_dev.png b/platform/crd-ctrlr/diagrams/sdewan_dev.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b18b7813a456c554c11ac67f286509ddb464cd GIT binary patch literal 198388 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

jy-tnfKF+AHIQhfQ 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 literal 0 HcmV?d00001 diff --git a/platform/crd-ctrlr/examples/README.md b/platform/crd-ctrlr/examples/README.md index 02df857..b21f456 100644 --- a/platform/crd-ctrlr/examples/README.md +++ b/platform/crd-ctrlr/examples/README.md @@ -1 +1,9 @@ # SDEWAN crd-ctrlr examples + +## To deploy an example CNF + +```shell +kubectl apply -f attach-network-ovn.yaml +kubectl apply -f ovn-net1.yaml ovn-net2.yml ovn-provnet.yaml sdewan-cm.yaml +kubectl apply -f sdewan-deployment.yaml +``` diff --git a/platform/crd-ctrlr/examples/attach-network-ovn.yaml b/platform/crd-ctrlr/examples/attach-network-ovn.yaml new file mode 100644 index 0000000..cb1102a --- /dev/null +++ b/platform/crd-ctrlr/examples/attach-network-ovn.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: k8s.cni.cncf.io/v1 +kind: NetworkAttachmentDefinition +metadata: + name: ovn-networkobj +spec: + config: '{ + "cniVersion": "0.3.1", + "name": "ovn4nfv-k8s-plugin", + "type": "ovn4nfvk8s-cni" + }' diff --git a/platform/crd-ctrlr/examples/cnf-deployment-older-than-1.16.yaml b/platform/crd-ctrlr/examples/cnf-deployment-older-than-1.16.yaml new file mode 100644 index 0000000..01c6726 --- /dev/null +++ b/platform/crd-ctrlr/examples/cnf-deployment-older-than-1.16.yaml @@ -0,0 +1,143 @@ +--- +apiVersion: v1 +data: + entrypoint.sh: |- + #!/bin/bash + # Always exit on errors. + set -ex + echo "" > /etc/config/network + cat > /etc/config/mwan3 <> /etc/config/network <> /etc/config/mwan3 < /etc/config/network + cat > /etc/config/mwan3 <> /etc/config/network <> /etc/config/mwan3 < ../examples/sdewan-controller.yaml + +# Generate manifests e.g. CRD, RBAC etc. +manifests: controller-gen + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +# Run go fmt against code +fmt: + go fmt ./... + +# Run go vet against code +vet: + go vet ./... + +# Generate code +generate: controller-gen + $(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..." + +# Build the docker image +docker-build: test + docker build . -t ${IMG} + +# Push the docker image +docker-push: + docker push ${IMG} + +# find or download controller-gen +# download controller-gen if necessary +controller-gen: +ifeq (, $(shell which controller-gen)) + @{ \ + set -e ;\ + CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ + cd $$CONTROLLER_GEN_TMP_DIR ;\ + go mod init tmp ;\ + go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.4 ;\ + rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ + } +CONTROLLER_GEN=$(GOBIN)/controller-gen +else +CONTROLLER_GEN=$(shell which controller-gen) +endif diff --git a/platform/crd-ctrlr/src/PROJECT b/platform/crd-ctrlr/src/PROJECT new file mode 100644 index 0000000..dbe80e8 --- /dev/null +++ b/platform/crd-ctrlr/src/PROJECT @@ -0,0 +1,7 @@ +domain: sdewan.akraino.org +repo: sdewan.akraino.org/sdewan +resources: +- group: batch + kind: Mwan3Policy + version: v1alpha1 +version: "2" diff --git a/platform/crd-ctrlr/src/README.md b/platform/crd-ctrlr/src/README.md deleted file mode 100644 index d1f9295..0000000 --- a/platform/crd-ctrlr/src/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Sdewan operator - -The sdewan operator is developed under kubebuilder framework - -We define two CRDs in this patch: Sdewan and Mwan3Conf. - -Sdewan defines the CNF base info, which node we should deploy the CNF on, -which network should the CNF use with multus CNI, etc. - -The Mwan3Conf defines the mwan3 rules. In the next step, we are going to -develop the firewall and the ipsec functions. Mwan3Conf is validated by k8s -api admission webhook. - -For each created Sdewan instance, the controller creates a pod, a configmap -and a service for the instance. The pod runs openwrt which provides network -services, i.e. sdwan, firewall, ipsec etc. - -The configmap stores the network interface information and the entrypoint.sh. -The network interface information has the following format: -``` -[ - { - "name": "ovn-priv-net", - "isProvider": false, - "interface": "net0", - "defaultGateway": false - } -] -``` - -The service created by the controller is used for openwrt api access. -We call this svc to apply rules, get openwrt info, restart openwrt service. - -After the openwrt pod ready, the Sdewan controller apply the configured mwan3 rules. -mwan3 rule details are configured in Mwan3Conf CR, which is referenced by Sdewan.Spec.Mwan3Conf -Every time the Mwan3Conf instance changes, the controller re-apply the new rules by calling opwnrt -api. We can also change the rule refernce at the runtime. - -## Deployment - -The API admission webhook depends on cert-manager so we need to install cert-manager first. - -To install the CRD and the controller, we can follow this guide. -https://book.kubebuilder.io/cronjob-tutorial/running-webhook.html - -We have the image built and published at `integratedcloudnative/sdewan-controller:dev`. The openwrt -docker image we used for test is at `integratedcloudnative/openwrt:dev`. To use some other images, -we need to make configuration in `config/default/manager_image_patch.yaml` - -The simple installation steps: -1. kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.11.0/cert-manager.yaml -2. kubectl apply -f sdewan-deploy.yaml - -## Create Sdewan CNF docker image -1. update build/set_proxy file with required proxy for docker build -2. execute below commands to generate Sdewan CNF docker image which tagged with 'openwrt-1806-mwan3' -``` -cd build -sudo bash build_image.sh -``` - -## References - -- https://book.kubebuilder.io/ -- https://openwrt.org/ diff --git a/platform/crd-ctrlr/src/api/v1alpha1/common_types.go b/platform/crd-ctrlr/src/api/v1alpha1/common_types.go new file mode 100644 index 0000000..8d59e86 --- /dev/null +++ b/platform/crd-ctrlr/src/api/v1alpha1/common_types.go @@ -0,0 +1,29 @@ +/* + +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" +) + +// status subsource used for Sdewan rule CRDs +type SdewanStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + AppliedVersion string `json:"appliedVersion"` + AppliedTime *metav1.Time `json:"appliedTime"` + InSync bool `json:"inSync"` +} diff --git a/platform/crd-ctrlr/src/api/v1alpha1/groupversion_info.go b/platform/crd-ctrlr/src/api/v1alpha1/groupversion_info.go new file mode 100644 index 0000000..2d9b3fe --- /dev/null +++ b/platform/crd-ctrlr/src/api/v1alpha1/groupversion_info.go @@ -0,0 +1,35 @@ +/* + +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 contains API Schema definitions for the batch v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=batch.sdewan.akraino.org +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "batch.sdewan.akraino.org", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/platform/crd-ctrlr/src/api/v1alpha1/mwan3policy_types.go b/platform/crd-ctrlr/src/api/v1alpha1/mwan3policy_types.go new file mode 100644 index 0000000..836e478 --- /dev/null +++ b/platform/crd-ctrlr/src/api/v1alpha1/mwan3policy_types.go @@ -0,0 +1,61 @@ +/* + +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. + +// Mwan3PolicySpec defines the desired state of Mwan3Policy +type Mwan3PolicyMember struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + Network string `json:"network"` + Metric int `json:"metric"` + Weight int `json:"weight"` +} + +type Mwan3PolicySpec struct { + Members []Mwan3PolicyMember `json:"members"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Mwan3Policy is the Schema for the mwan3policies API +type Mwan3Policy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec Mwan3PolicySpec `json:"spec,omitempty"` + Status SdewanStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// Mwan3PolicyList contains a list of Mwan3Policy +type Mwan3PolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Mwan3Policy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Mwan3Policy{}, &Mwan3PolicyList{}) +} diff --git a/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go b/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..1e42bbb --- /dev/null +++ b/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,137 @@ +// +build !ignore_autogenerated + +/* + +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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Mwan3Policy) DeepCopyInto(out *Mwan3Policy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3Policy. +func (in *Mwan3Policy) DeepCopy() *Mwan3Policy { + if in == nil { + return nil + } + out := new(Mwan3Policy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Mwan3Policy) 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 *Mwan3PolicyList) DeepCopyInto(out *Mwan3PolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Mwan3Policy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3PolicyList. +func (in *Mwan3PolicyList) DeepCopy() *Mwan3PolicyList { + if in == nil { + return nil + } + out := new(Mwan3PolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Mwan3PolicyList) 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 *Mwan3PolicyMember) DeepCopyInto(out *Mwan3PolicyMember) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3PolicyMember. +func (in *Mwan3PolicyMember) DeepCopy() *Mwan3PolicyMember { + if in == nil { + return nil + } + out := new(Mwan3PolicyMember) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Mwan3PolicySpec) DeepCopyInto(out *Mwan3PolicySpec) { + *out = *in + if in.Members != nil { + in, out := &in.Members, &out.Members + *out = make([]Mwan3PolicyMember, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3PolicySpec. +func (in *Mwan3PolicySpec) DeepCopy() *Mwan3PolicySpec { + if in == nil { + return nil + } + out := new(Mwan3PolicySpec) + 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 + if in.AppliedTime != nil { + in, out := &in.AppliedTime, &out.AppliedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SdewanStatus. +func (in *SdewanStatus) DeepCopy() *SdewanStatus { + if in == nil { + return nil + } + out := new(SdewanStatus) + in.DeepCopyInto(out) + return out +} diff --git a/platform/crd-ctrlr/src/cnfprovider/cnfprovider.go b/platform/crd-ctrlr/src/cnfprovider/cnfprovider.go new file mode 100644 index 0000000..b4ffd97 --- /dev/null +++ b/platform/crd-ctrlr/src/cnfprovider/cnfprovider.go @@ -0,0 +1,12 @@ +package cnfprovider + +import ( + sdewanv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1" +) + +type CnfProvider interface { + AddUpdateMwan3Policy(*sdewanv1alpha1.Mwan3Policy) error + DeleteMwan3Policy(*sdewanv1alpha1.Mwan3Policy) error + // TODO: Add more Interfaces here + IsCnfReady() (bool, error) +} diff --git a/platform/crd-ctrlr/src/cnfprovider/wrtprovider.go b/platform/crd-ctrlr/src/cnfprovider/wrtprovider.go new file mode 100644 index 0000000..63e514f --- /dev/null +++ b/platform/crd-ctrlr/src/cnfprovider/wrtprovider.go @@ -0,0 +1,174 @@ +package cnfprovider + +import ( + "context" + "encoding/json" + "errors" + "fmt" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "reflect" + sdewanv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1" + "sdewan.akraino.org/sdewan/openwrt" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "strconv" +) + +var log = logf.Log.WithName("wrt_provider") + +type WrtProvider struct { + Namespace string + SdewanPurpose string + Deployment appsv1.Deployment + K8sClient client.Client +} + +func NewWrt(namespace string, sdewanPurpose string, k8sClient client.Client) (*WrtProvider, error) { + reqLogger := log.WithValues("namespace", namespace, "sdewanPurpose", sdewanPurpose) + ctx := context.Background() + deployments := &appsv1.DeploymentList{} + err := k8sClient.List(ctx, deployments, client.MatchingLabels{"sdewanPurpose": sdewanPurpose}) + if err != nil { + reqLogger.Error(err, "Failed to get cnf deployment") + return nil, client.IgnoreNotFound(err) + } + if len(deployments.Items) <= 0 { + reqLogger.Info("No deployment exists") + return nil, nil + } + if len(deployments.Items) > 1 { + reqLogger.Error(nil, "More than one deployment exists") + return nil, errors.New("More than one deployment exists") + } + + return &WrtProvider{namespace, sdewanPurpose, deployments.Items[0], k8sClient}, nil +} + +func (p *WrtProvider) net2iface(net string) (string, error) { + type Iface struct { + DefaultGateway bool `json:"defaultGateway,string"` + Interface string + Name string + } + type NfnNet struct { + Type string + Interface []Iface + } + ann := p.Deployment.Spec.Template.Annotations + nfnNet := NfnNet{} + err := json.Unmarshal([]byte(ann["k8s.plugin.opnfv.org/nfn-network"]), &nfnNet) + if err != nil { + return "", err + } + for _, iface := range nfnNet.Interface { + if iface.Name == net { + return iface.Interface, nil + } + } + return "", errors.New(fmt.Sprintf("No matched network in annotation: %s", net)) + +} + +func (p *WrtProvider) convertCrd(mwan3Policy *sdewanv1alpha1.Mwan3Policy) (*openwrt.SdewanPolicy, error) { + members := make([]openwrt.SdewanMember, len(mwan3Policy.Spec.Members)) + for i, membercr := range mwan3Policy.Spec.Members { + iface, err := p.net2iface(membercr.Network) + if err != nil { + return nil, err + } + members[i] = openwrt.SdewanMember{ + Interface: iface, + Metric: strconv.Itoa(membercr.Metric), + Weight: strconv.Itoa(membercr.Weight), + } + } + return &openwrt.SdewanPolicy{Name: mwan3Policy.Name, Members: members}, nil + +} + +func (p *WrtProvider) AddUpdateMwan3Policy(mwan3Policy *sdewanv1alpha1.Mwan3Policy) (bool, error) { + reqLogger := log.WithValues("Mwan3Policy", mwan3Policy.Name, "cnf", p.Deployment.Name) + ctx := context.Background() + podList := &corev1.PodList{} + err := p.K8sClient.List(ctx, podList, client.MatchingLabels{"sdewanPurpose": p.SdewanPurpose}) + if err != nil { + reqLogger.Error(err, "Failed to get cnf pod list") + return false, err + } + policy, err := p.convertCrd(mwan3Policy) + if err != nil { + reqLogger.Error(err, "Failed to convert mwan3Policy CR") + return false, err + } + cnfChanged := false + for _, pod := range podList.Items { + openwrtClient := openwrt.NewOpenwrtClient(pod.Status.PodIP, "root", "") + mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient} + service := openwrt.ServiceClient{OpenwrtClient: openwrtClient} + runtimePolicy, _ := mwan3.GetPolicy(policy.Name) + changed := false + if runtimePolicy == nil { + _, err := mwan3.CreatePolicy(*policy) + if err != nil { + reqLogger.Error(err, "Failed to create policy") + return false, err + } + changed = true + } else if reflect.DeepEqual(*runtimePolicy, *policy) { + reqLogger.Info("Equal to the runtime policy, so no update") + } else { + _, err := mwan3.UpdatePolicy(*policy) + if err != nil { + reqLogger.Error(err, "Failed to update policy") + return false, err + } + changed = true + } + if changed { + _, err = service.ExecuteService("mwan3", "restart") + if err != nil { + reqLogger.Error(err, "Failed to restart mwan3 service") + return changed, err + } + cnfChanged = true + } + } + // We say the AddUpdate succeed only when the add/update for all pods succeed + return cnfChanged, nil +} + +func (p *WrtProvider) DeleteMwan3Policy(mwan3Policy *sdewanv1alpha1.Mwan3Policy) (bool, error) { + reqLogger := log.WithValues("Mwan3Policy", mwan3Policy.Name, "cnf", p.Deployment.Name) + ctx := context.Background() + podList := &corev1.PodList{} + err := p.K8sClient.List(ctx, podList, client.MatchingLabels{"sdewanPurpose": p.SdewanPurpose}) + if err != nil { + reqLogger.Error(err, "Failed to get pod list") + return false, err + } + cnfChanged := false + for _, pod := range podList.Items { + openwrtClient := openwrt.NewOpenwrtClient(pod.Status.PodIP, "root", "") + mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient} + service := openwrt.ServiceClient{OpenwrtClient: openwrtClient} + runtimePolicy, _ := mwan3.GetPolicy(mwan3Policy.Name) + if runtimePolicy == nil { + reqLogger.Info("Runtime policy doesn't exist, so don't have to delete") + } else { + err = mwan3.DeletePolicy(mwan3Policy.Name) + if err != nil { + reqLogger.Error(err, "Failed to delete policy") + return false, err + } + _, err = service.ExecuteService("mwan3", "restart") + if err != nil { + reqLogger.Error(err, "Failed to restart mwan3 service") + return false, err + } + cnfChanged = true + } + } + // We say the deletioni succeed only when the deletion for all pods succeed + return cnfChanged, nil +} diff --git a/platform/crd-ctrlr/src/config/certmanager/certificate.yaml b/platform/crd-ctrlr/src/config/certmanager/certificate.yaml new file mode 100644 index 0000000..237c937 --- /dev/null +++ b/platform/crd-ctrlr/src/config/certmanager/certificate.yaml @@ -0,0 +1,25 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for breaking changes +apiVersion: cert-manager.io/v1alpha2 +kind: Issuer +metadata: + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/platform/crd-ctrlr/src/config/certmanager/kustomization.yaml b/platform/crd-ctrlr/src/config/certmanager/kustomization.yaml new file mode 100644 index 0000000..bebea5a --- /dev/null +++ b/platform/crd-ctrlr/src/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/platform/crd-ctrlr/src/config/certmanager/kustomizeconfig.yaml b/platform/crd-ctrlr/src/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 0000000..90d7c31 --- /dev/null +++ b/platform/crd-ctrlr/src/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_mwan3policies.yaml b/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_mwan3policies.yaml new file mode 100644 index 0000000..09b4f5e --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_mwan3policies.yaml @@ -0,0 +1,89 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.4 + creationTimestamp: null + name: mwan3policies.batch.sdewan.akraino.org +spec: + group: batch.sdewan.akraino.org + names: + kind: Mwan3Policy + listKind: Mwan3PolicyList + plural: mwan3policies + singular: mwan3policy + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: Mwan3Policy is the Schema for the mwan3policies 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: + properties: + members: + items: + description: Mwan3PolicySpec defines the desired state of Mwan3Policy + properties: + metric: + type: integer + network: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of + cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + weight: + type: integer + required: + - metric + - network + - weight + type: object + type: array + required: + - members + type: object + status: + description: status subsource used for Sdewan rule CRDs + properties: + appliedTime: + format: date-time + type: string + appliedVersion: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + inSync: + type: boolean + required: + - appliedTime + - appliedVersion + - inSync + 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 new file mode 100644 index 0000000..ead3f7d --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/batch.sdewan.akraino.org_mwan3policies.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_mwan3policies.yaml +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_mwan3policies.yaml +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/platform/crd-ctrlr/src/config/crd/kustomizeconfig.yaml b/platform/crd-ctrlr/src/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000..6f83d9a --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/kustomizeconfig.yaml @@ -0,0 +1,17 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + group: apiextensions.k8s.io + path: spec/conversion/webhookClientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_mwan3policies.yaml b/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_mwan3policies.yaml new file mode 100644 index 0000000..2ba4595 --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_mwan3policies.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: mwan3policies.batch.sdewan.akraino.org diff --git a/platform/crd-ctrlr/src/config/crd/patches/webhook_in_mwan3policies.yaml b/platform/crd-ctrlr/src/config/crd/patches/webhook_in_mwan3policies.yaml new file mode 100644 index 0000000..3cd0686 --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/patches/webhook_in_mwan3policies.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: mwan3policies.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/default/kustomization.yaml b/platform/crd-ctrlr/src/config/default/kustomization.yaml new file mode 100644 index 0000000..ef12fbf --- /dev/null +++ b/platform/crd-ctrlr/src/config/default/kustomization.yaml @@ -0,0 +1,74 @@ +# Adds namespace to all resources. +namespace: sdewan-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: sdewan- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +bases: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: + # Protect the /metrics endpoint by putting it behind auth. + # Only one of manager_auth_proxy_patch.yaml and + # manager_prometheus_metrics_patch.yaml should be enabled. +- manager_auth_proxy_patch.yaml + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, uncomment the following line and + # comment manager_auth_proxy_patch.yaml. + # Only one of manager_auth_proxy_patch.yaml and + # manager_prometheus_metrics_patch.yaml should be enabled. +#- manager_prometheus_metrics_patch.yaml + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +vars: +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1alpha2 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/platform/crd-ctrlr/src/config/default/manager_auth_proxy_patch.yaml b/platform/crd-ctrlr/src/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 0000000..61cb5e7 --- /dev/null +++ b/platform/crd-ctrlr/src/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,25 @@ +# This patch inject a sidecar container which is a HTTP proxy for the controller manager, +# it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.1 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" diff --git a/platform/crd-ctrlr/src/config/default/manager_webhook_patch.yaml b/platform/crd-ctrlr/src/config/default/manager_webhook_patch.yaml new file mode 100644 index 0000000..738de35 --- /dev/null +++ b/platform/crd-ctrlr/src/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/platform/crd-ctrlr/src/config/default/webhookcainjection_patch.yaml b/platform/crd-ctrlr/src/config/default/webhookcainjection_patch.yaml new file mode 100644 index 0000000..7e79bf9 --- /dev/null +++ b/platform/crd-ctrlr/src/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,15 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/platform/crd-ctrlr/src/config/manager/kustomization.yaml b/platform/crd-ctrlr/src/config/manager/kustomization.yaml new file mode 100644 index 0000000..1b69499 --- /dev/null +++ b/platform/crd-ctrlr/src/config/manager/kustomization.yaml @@ -0,0 +1,8 @@ +resources: +- manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: integratedcloudnative/sdewan-controller + newTag: dev diff --git a/platform/crd-ctrlr/src/config/manager/manager.yaml b/platform/crd-ctrlr/src/config/manager/manager.yaml new file mode 100644 index 0000000..b6c85a5 --- /dev/null +++ b/platform/crd-ctrlr/src/config/manager/manager.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - command: + - /manager + args: + - --enable-leader-election + image: controller:latest + name: manager + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + terminationGracePeriodSeconds: 10 diff --git a/platform/crd-ctrlr/src/config/prometheus/kustomization.yaml b/platform/crd-ctrlr/src/config/prometheus/kustomization.yaml new file mode 100644 index 0000000..ed13716 --- /dev/null +++ b/platform/crd-ctrlr/src/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/platform/crd-ctrlr/src/config/prometheus/monitor.yaml b/platform/crd-ctrlr/src/config/prometheus/monitor.yaml new file mode 100644 index 0000000..e2d9b08 --- /dev/null +++ b/platform/crd-ctrlr/src/config/prometheus/monitor.yaml @@ -0,0 +1,15 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + selector: + control-plane: controller-manager diff --git a/platform/crd-ctrlr/src/config/rbac/auth_proxy_role.yaml b/platform/crd-ctrlr/src/config/rbac/auth_proxy_role.yaml new file mode 100644 index 0000000..618f5e4 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: proxy-role +rules: +- apiGroups: ["authentication.k8s.io"] + resources: + - tokenreviews + verbs: ["create"] +- apiGroups: ["authorization.k8s.io"] + resources: + - subjectaccessreviews + verbs: ["create"] diff --git a/platform/crd-ctrlr/src/config/rbac/auth_proxy_role_binding.yaml b/platform/crd-ctrlr/src/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 0000000..48ed1e4 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/platform/crd-ctrlr/src/config/rbac/auth_proxy_service.yaml b/platform/crd-ctrlr/src/config/rbac/auth_proxy_service.yaml new file mode 100644 index 0000000..6cf656b --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/platform/crd-ctrlr/src/config/rbac/kustomization.yaml b/platform/crd-ctrlr/src/config/rbac/kustomization.yaml new file mode 100644 index 0000000..817f1fe --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/kustomization.yaml @@ -0,0 +1,11 @@ +resources: +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 3 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml diff --git a/platform/crd-ctrlr/src/config/rbac/leader_election_role.yaml b/platform/crd-ctrlr/src/config/rbac/leader_election_role.yaml new file mode 100644 index 0000000..eaa7915 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/leader_election_role.yaml @@ -0,0 +1,32 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - create diff --git a/platform/crd-ctrlr/src/config/rbac/leader_election_role_binding.yaml b/platform/crd-ctrlr/src/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 0000000..eed1690 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/platform/crd-ctrlr/src/config/rbac/mwan3policy_editor_role.yaml b/platform/crd-ctrlr/src/config/rbac/mwan3policy_editor_role.yaml new file mode 100644 index 0000000..6a4fa45 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/mwan3policy_editor_role.yaml @@ -0,0 +1,26 @@ +# permissions to do edit mwan3policies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: mwan3policy-editor-role +rules: +- apiGroups: + - batch.sdewan.akraino.org + resources: + - mwan3policies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch.sdewan.akraino.org + resources: + - mwan3policies/status + verbs: + - get + - patch + - update diff --git a/platform/crd-ctrlr/src/config/rbac/mwan3policy_viewer_role.yaml b/platform/crd-ctrlr/src/config/rbac/mwan3policy_viewer_role.yaml new file mode 100644 index 0000000..98c70b6 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/mwan3policy_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions to do viewer mwan3policies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: mwan3policy-viewer-role +rules: +- apiGroups: + - batch.sdewan.akraino.org + resources: + - mwan3policies + verbs: + - get + - list + - watch +- apiGroups: + - batch.sdewan.akraino.org + resources: + - mwan3policies/status + verbs: + - get diff --git a/platform/crd-ctrlr/src/config/rbac/role.yaml b/platform/crd-ctrlr/src/config/rbac/role.yaml new file mode 100644 index 0000000..d5f67ee --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/role.yaml @@ -0,0 +1,36 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - batch.sdewan.akraino.org + resources: + - mwan3policies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch.sdewan.akraino.org + resources: + - mwan3policies/status + verbs: + - get + - patch + - update +- apiGroups: + - extensions + resources: + - deployments + verbs: + - get + - list + - watch diff --git a/platform/crd-ctrlr/src/config/rbac/role_binding.yaml b/platform/crd-ctrlr/src/config/rbac/role_binding.yaml new file mode 100644 index 0000000..8f26587 --- /dev/null +++ b/platform/crd-ctrlr/src/config/rbac/role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: default + namespace: system diff --git a/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_mwan3policy.yaml b/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_mwan3policy.yaml new file mode 100644 index 0000000..fd6cb16 --- /dev/null +++ b/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_mwan3policy.yaml @@ -0,0 +1,15 @@ +apiVersion: batch.sdewan.akraino.org/v1alpha1 +kind: Mwan3Policy +metadata: + name: balance1 + namespace: default + labels: + sdewanPurpose: cnf1 +spec: + members: + - network: ovn-net1 + weight: 4 + metric: 2 + - network: ovn-net2 + weight: 3 + metric: 3 diff --git a/platform/crd-ctrlr/src/config/webhook/kustomization.yaml b/platform/crd-ctrlr/src/config/webhook/kustomization.yaml new file mode 100644 index 0000000..9cf2613 --- /dev/null +++ b/platform/crd-ctrlr/src/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/platform/crd-ctrlr/src/config/webhook/kustomizeconfig.yaml b/platform/crd-ctrlr/src/config/webhook/kustomizeconfig.yaml new file mode 100644 index 0000000..25e21e3 --- /dev/null +++ b/platform/crd-ctrlr/src/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/platform/crd-ctrlr/src/config/webhook/manifests.yaml b/platform/crd-ctrlr/src/config/webhook/manifests.yaml new file mode 100644 index 0000000..e69de29 diff --git a/platform/crd-ctrlr/src/config/webhook/service.yaml b/platform/crd-ctrlr/src/config/webhook/service.yaml new file mode 100644 index 0000000..31e0f82 --- /dev/null +++ b/platform/crd-ctrlr/src/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/platform/crd-ctrlr/src/controllers/mwan3policy_controller.go b/platform/crd-ctrlr/src/controllers/mwan3policy_controller.go new file mode 100644 index 0000000..842f316 --- /dev/null +++ b/platform/crd-ctrlr/src/controllers/mwan3policy_controller.go @@ -0,0 +1,149 @@ +/* + +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" + "time" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1" + "sdewan.akraino.org/sdewan/cnfprovider" +) + +// Mwan3PolicyReconciler reconciles a Mwan3Policy object +type Mwan3PolicyReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=mwan3policies,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=mwan3policies/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=extensions,resources=deployments,verbs=get;list;watch + +func (r *Mwan3PolicyReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + ctx := context.Background() + log := r.Log.WithValues("mwan3policy", req.NamespacedName) + + // your logic here + during, _ := time.ParseDuration("5s") + instance := &batchv1alpha1.Mwan3Policy{} + err := r.Get(ctx, req.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // No instance + return ctrl.Result{}, nil + } + // Error reading the object - requeue the request. + return ctrl.Result{RequeueAfter: during}, nil + } + cnf, err := cnfprovider.NewWrt(req.NamespacedName.Namespace, instance.Labels["sdewanPurpose"], r.Client) + if err != nil { + log.Error(err, "Failed to get cnf") + // A new event are supposed to be received upon cnf ready + // so not requeue + return ctrl.Result{}, nil + } + finalizerName := "rule.finalizers.sdewan.akraino.org" + if instance.ObjectMeta.DeletionTimestamp.IsZero() { + // creating or updating CR + if cnf == nil { + // no cnf exists + log.Info("No cnf exist, so not create/update mwan3 policy") + return ctrl.Result{}, nil + } + changed, err := cnf.AddUpdateMwan3Policy(instance) + if err != nil { + log.Error(err, "Failed to add/update mwan3 policy") + return ctrl.Result{RequeueAfter: during}, nil + } + if !containsString(instance.ObjectMeta.Finalizers, finalizerName) { + log.Info("Adding finalizer for mwan3 policy") + instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, finalizerName) + if err := r.Update(ctx, instance); err != nil { + return ctrl.Result{}, err + } + } + if changed { + instance.Status.AppliedVersion = instance.ResourceVersion + instance.Status.AppliedTime = &metav1.Time{Time: time.Now()} + instance.Status.InSync = true + err = r.Status().Update(ctx, instance) + if err != nil { + log.Error(err, "Failed to update mwan3 policy status") + return ctrl.Result{}, err + } + } + } else { + // deletin CR + if cnf == nil { + // no cnf exists + if containsString(instance.ObjectMeta.Finalizers, finalizerName) { + instance.ObjectMeta.Finalizers = removeString(instance.ObjectMeta.Finalizers, finalizerName) + if err := r.Update(ctx, instance); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + _, err := cnf.DeleteMwan3Policy(instance) + if err != nil { + log.Error(err, "Failed to delete mwan3 policy") + return ctrl.Result{RequeueAfter: during}, nil + } + if containsString(instance.ObjectMeta.Finalizers, finalizerName) { + instance.ObjectMeta.Finalizers = removeString(instance.ObjectMeta.Finalizers, finalizerName) + if err := r.Update(ctx, instance); err != nil { + return ctrl.Result{}, err + } + } + } + + return ctrl.Result{}, nil +} + +func (r *Mwan3PolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&batchv1alpha1.Mwan3Policy{}). + Complete(r) +} + +// Helper functions to check and remove string from a slice of strings. +func containsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + return false +} + +func removeString(slice []string, s string) (result []string) { + for _, item := range slice { + if item == s { + continue + } + result = append(result, item) + } + return +} diff --git a/platform/crd-ctrlr/src/controllers/suite_test.go b/platform/crd-ctrlr/src/controllers/suite_test.go new file mode 100644 index 0000000..f02eb83 --- /dev/null +++ b/platform/crd-ctrlr/src/controllers/suite_test.go @@ -0,0 +1,79 @@ +/* + +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 ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{envtest.NewlineReporter{}}) +} + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = batchv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + close(done) +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/platform/crd-ctrlr/src/go.mod b/platform/crd-ctrlr/src/go.mod new file mode 100644 index 0000000..4b67fd8 --- /dev/null +++ b/platform/crd-ctrlr/src/go.mod @@ -0,0 +1,13 @@ +module sdewan.akraino.org/sdewan + +go 1.14 + +require ( + github.com/go-logr/logr v0.1.0 + github.com/onsi/ginkgo v1.8.0 + github.com/onsi/gomega v1.5.0 + k8s.io/api v0.0.0-20190918155943-95b840bb6a1f + k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 + k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 + sigs.k8s.io/controller-runtime v0.4.0 +) diff --git a/platform/crd-ctrlr/src/go.sum b/platform/crd-ctrlr/src/go.sum new file mode 100644 index 0000000..9e3159d --- /dev/null +++ b/platform/crd-ctrlr/src/go.sum @@ -0,0 +1,381 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= +github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= +golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.0.0-20190918155943-95b840bb6a1f h1:8FRUST8oUkEI45WYKyD8ed7Ad0Kg5v11zHyPkEVb2xo= +k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= +k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783 h1:V6ndwCPoao1yZ52agqOKaUAl7DYWVGiXjV7ePA2i610= +k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY= +k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 h1:CS1tBQz3HOXiseWZu6ZicKX361CZLT97UFnnPx0aqBw= +k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= +k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg= +k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 h1:mLmhKUm1X+pXu0zXMEzNsOF5E2kKFGe5o6BZBIIqA6A= +k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= +k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE= +k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= +k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg= +sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= +sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM= +sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/platform/crd-ctrlr/src/hack/boilerplate.go.txt b/platform/crd-ctrlr/src/hack/boilerplate.go.txt new file mode 100644 index 0000000..b92001f --- /dev/null +++ b/platform/crd-ctrlr/src/hack/boilerplate.go.txt @@ -0,0 +1,14 @@ +/* + +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. +*/ \ No newline at end of file diff --git a/platform/crd-ctrlr/src/main.go b/platform/crd-ctrlr/src/main.go new file mode 100644 index 0000000..b67302d --- /dev/null +++ b/platform/crd-ctrlr/src/main.go @@ -0,0 +1,82 @@ +/* + +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 main + +import ( + "flag" + "os" + + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1" + "sdewan.akraino.org/sdewan/controllers" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + _ = clientgoscheme.AddToScheme(scheme) + + _ = batchv1alpha1.AddToScheme(scheme) + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, + "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.") + flag.Parse() + + ctrl.SetLogger(zap.New(func(o *zap.Options) { + o.Development = true + })) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + LeaderElection: enableLeaderElection, + Port: 9443, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + if err = (&controllers.Mwan3PolicyReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("Mwan3Policy"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Mwan3Policy") + os.Exit(1) + } + // +kubebuilder:scaffold:builder + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/platform/crd-ctrlr/src/openwrt/firewall.go b/platform/crd-ctrlr/src/openwrt/firewall.go new file mode 100644 index 0000000..4bc16a7 --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/firewall.go @@ -0,0 +1,437 @@ +package openwrt + +import ( + "encoding/json" +) + +const ( + firewallBaseURL = "sdewan/firewall/v1/" +) + +type FirewallClient struct { + OpenwrtClient *openwrtClient +} + +// Firewall Zones +type SdewanFirewallZone struct { + Name string `json:"name"` + Network []string `json:"network"` + Masq string `json:"masq"` + MasqSrc []string `json:"masq_src"` + MasqDest []string `json:"masq_dest"` + MasqAllowInvalid string `json:"masq_allow_invalid"` + MtuFix string `json:"mtu_fix"` + Input string `json:"input"` + Forward string `json:"forward"` + Output string `json:"output"` + Family string `json:"family"` + Subnet []string `json:"subnet"` + ExtraSrc string `json:"extra_src"` + ExtraDest string `json:"etra_dest"` +} + +type SdewanFirewallZones struct { + Zones []SdewanFirewallZone `json:"zones"` +} + +// Firewall Forwarding +type SdewanFirewallForwarding struct { + Name string `json:"name"` + Src string `json:"src"` + Dest string `json:"dest"` + Family string `json:"family"` +} + +type SdewanFirewallForwardings struct { + Forwardings []SdewanFirewallForwarding `json:"forwardings"` +} + +// Firewall Rule +type SdewanFirewallRule struct { + Name string `json:"name"` + Src string `json:"src"` + SrcIp string `json:"src_ip"` + SrcMac string `json:"src_mac"` + SrcPort string `json:"src_port"` + Proto string `json:"proto"` + IcmpType []string `json:"icmp_type"` + Dest string `json:"dest"` + DestIp string `json:"dest_ip"` + DestPort string `json:"dest_port"` + Mark string `json:"mark"` + Target string `json:"target"` + SetMark string `json:"set_mark"` + SetXmark string `json:"set_xmark"` + Family string `json:"family"` + Extra string `json:"extra"` +} + +type SdewanFirewallRules struct { + Rules []SdewanFirewallRule `json:"rules"` +} + +// Firewall Redirect +type SdewanFirewallRedirect struct { + Name string `json:"name"` + Src string `json:"src"` + SrcIp string `json:"src_ip"` + SrcDIp string `json:"src_dip"` + SrcMac string `json:"src_mac"` + SrcPort string `json:"src_port"` + SrcDPort string `json:"src_dport"` + Proto string `json:"proto"` + Dest string `json:"dest"` + DestIp string `json:"dest_ip"` + DestPort string `json:"dest_port"` + Mark string `json:"mark"` + Target string `json:"target"` + Family string `json:"family"` +} + +type SdewanFirewallRedirects struct { + Redirects []SdewanFirewallRedirect `json:"redirects"` +} + +// Zone APIs +// get zones +func (f *FirewallClient) GetZones() (*SdewanFirewallZones, error) { + var response string + var err error + response, err = f.OpenwrtClient.Get(firewallBaseURL + "zones") + if err != nil { + return nil, err + } + + var sdewanFirewallZones SdewanFirewallZones + err = json.Unmarshal([]byte(response), &sdewanFirewallZones) + if err != nil { + return nil, err + } + + return &sdewanFirewallZones, nil +} + +// get zone +func (m *FirewallClient) GetZone(zone string) (*SdewanFirewallZone, error) { + var response string + var err error + response, err = m.OpenwrtClient.Get(firewallBaseURL + "zone/" + zone) + if err != nil { + return nil, err + } + + var sdewanFirewallZone SdewanFirewallZone + err = json.Unmarshal([]byte(response), &sdewanFirewallZone) + if err != nil { + return nil, err + } + + return &sdewanFirewallZone, nil +} + +// create zone +func (m *FirewallClient) CreateZone(zone SdewanFirewallZone) (*SdewanFirewallZone, error) { + var response string + var err error + zone_obj, _ := json.Marshal(zone) + response, err = m.OpenwrtClient.Post(firewallBaseURL+"zone", string(zone_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallZone SdewanFirewallZone + err = json.Unmarshal([]byte(response), &sdewanFirewallZone) + if err != nil { + return nil, err + } + + return &sdewanFirewallZone, nil +} + +// delete zone +func (m *FirewallClient) DeleteZone(zone_name string) error { + _, err := m.OpenwrtClient.Delete(firewallBaseURL + "zone/" + zone_name) + if err != nil { + return err + } + + return nil +} + +// update zone +func (m *FirewallClient) UpdateZone(zone SdewanFirewallZone) (*SdewanFirewallZone, error) { + var response string + var err error + zone_obj, _ := json.Marshal(zone) + zone_name := zone.Name + response, err = m.OpenwrtClient.Put(firewallBaseURL+"zone/"+zone_name, string(zone_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallZone SdewanFirewallZone + err = json.Unmarshal([]byte(response), &sdewanFirewallZone) + if err != nil { + return nil, err + } + + return &sdewanFirewallZone, nil +} + +// Rule APIs +// get rules +func (f *FirewallClient) GetRules() (*SdewanFirewallRules, error) { + var response string + var err error + response, err = f.OpenwrtClient.Get(firewallBaseURL + "rules") + if err != nil { + return nil, err + } + + var sdewanFirewallRules SdewanFirewallRules + err = json.Unmarshal([]byte(response), &sdewanFirewallRules) + if err != nil { + return nil, err + } + + return &sdewanFirewallRules, nil +} + +// get rule +func (m *FirewallClient) GetRule(rule string) (*SdewanFirewallRule, error) { + var response string + var err error + response, err = m.OpenwrtClient.Get(firewallBaseURL + "rule/" + rule) + if err != nil { + return nil, err + } + + var sdewanFirewallRule SdewanFirewallRule + err = json.Unmarshal([]byte(response), &sdewanFirewallRule) + if err != nil { + return nil, err + } + + return &sdewanFirewallRule, nil +} + +// create rule +func (m *FirewallClient) CreateRule(rule SdewanFirewallRule) (*SdewanFirewallRule, error) { + var response string + var err error + rule_obj, _ := json.Marshal(rule) + response, err = m.OpenwrtClient.Post(firewallBaseURL+"rule", string(rule_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallRule SdewanFirewallRule + err = json.Unmarshal([]byte(response), &sdewanFirewallRule) + if err != nil { + return nil, err + } + + return &sdewanFirewallRule, nil +} + +// delete rule +func (m *FirewallClient) DeleteRule(rule_name string) error { + _, err := m.OpenwrtClient.Delete(firewallBaseURL + "rule/" + rule_name) + if err != nil { + return err + } + + return nil +} + +// update rule +func (m *FirewallClient) UpdateRule(rule SdewanFirewallRule) (*SdewanFirewallRule, error) { + var response string + var err error + rule_obj, _ := json.Marshal(rule) + rule_name := rule.Name + response, err = m.OpenwrtClient.Put(firewallBaseURL+"rule/"+rule_name, string(rule_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallRule SdewanFirewallRule + err = json.Unmarshal([]byte(response), &sdewanFirewallRule) + if err != nil { + return nil, err + } + + return &sdewanFirewallRule, nil +} + +// Forwarding APIs +// get forwardings +func (f *FirewallClient) GetForwardings() (*SdewanFirewallForwardings, error) { + var response string + var err error + response, err = f.OpenwrtClient.Get(firewallBaseURL + "forwardings") + if err != nil { + return nil, err + } + + var sdewanFirewallForwardings SdewanFirewallForwardings + err = json.Unmarshal([]byte(response), &sdewanFirewallForwardings) + if err != nil { + return nil, err + } + + return &sdewanFirewallForwardings, nil +} + +// get forwarding +func (m *FirewallClient) GetForwarding(forwarding string) (*SdewanFirewallForwarding, error) { + var response string + var err error + response, err = m.OpenwrtClient.Get(firewallBaseURL + "forwarding/" + forwarding) + if err != nil { + return nil, err + } + + var sdewanFirewallForwarding SdewanFirewallForwarding + err = json.Unmarshal([]byte(response), &sdewanFirewallForwarding) + if err != nil { + return nil, err + } + + return &sdewanFirewallForwarding, nil +} + +// create forwarding +func (m *FirewallClient) CreateForwarding(forwarding SdewanFirewallForwarding) (*SdewanFirewallForwarding, error) { + var response string + var err error + forwarding_obj, _ := json.Marshal(forwarding) + response, err = m.OpenwrtClient.Post(firewallBaseURL+"forwarding", string(forwarding_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallForwarding SdewanFirewallForwarding + err = json.Unmarshal([]byte(response), &sdewanFirewallForwarding) + if err != nil { + return nil, err + } + + return &sdewanFirewallForwarding, nil +} + +// delete forwarding +func (m *FirewallClient) DeleteForwarding(forwarding_name string) error { + _, err := m.OpenwrtClient.Delete(firewallBaseURL + "forwarding/" + forwarding_name) + if err != nil { + return err + } + + return nil +} + +// update forwarding +func (m *FirewallClient) UpdateForwarding(forwarding SdewanFirewallForwarding) (*SdewanFirewallForwarding, error) { + var response string + var err error + forwarding_obj, _ := json.Marshal(forwarding) + forwarding_name := forwarding.Name + response, err = m.OpenwrtClient.Put(firewallBaseURL+"forwarding/"+forwarding_name, string(forwarding_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallForwarding SdewanFirewallForwarding + err = json.Unmarshal([]byte(response), &sdewanFirewallForwarding) + if err != nil { + return nil, err + } + + return &sdewanFirewallForwarding, nil +} + +// Redirect APIs +// get redirects +func (f *FirewallClient) GetRedirects() (*SdewanFirewallRedirects, error) { + var response string + var err error + response, err = f.OpenwrtClient.Get(firewallBaseURL + "redirects") + if err != nil { + return nil, err + } + + var sdewanFirewallRedirects SdewanFirewallRedirects + err = json.Unmarshal([]byte(response), &sdewanFirewallRedirects) + if err != nil { + return nil, err + } + + return &sdewanFirewallRedirects, nil +} + +// get redirect +func (m *FirewallClient) GetRedirect(redirect string) (*SdewanFirewallRedirect, error) { + var response string + var err error + response, err = m.OpenwrtClient.Get(firewallBaseURL + "redirect/" + redirect) + if err != nil { + return nil, err + } + + var sdewanFirewallRedirect SdewanFirewallRedirect + err = json.Unmarshal([]byte(response), &sdewanFirewallRedirect) + if err != nil { + return nil, err + } + + return &sdewanFirewallRedirect, nil +} + +// create redirect +func (m *FirewallClient) CreateRedirect(redirect SdewanFirewallRedirect) (*SdewanFirewallRedirect, error) { + var response string + var err error + redirect_obj, _ := json.Marshal(redirect) + response, err = m.OpenwrtClient.Post(firewallBaseURL+"redirect", string(redirect_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallRedirect SdewanFirewallRedirect + err = json.Unmarshal([]byte(response), &sdewanFirewallRedirect) + if err != nil { + return nil, err + } + + return &sdewanFirewallRedirect, nil +} + +// delete redirect +func (m *FirewallClient) DeleteRedirect(redirect_name string) error { + _, err := m.OpenwrtClient.Delete(firewallBaseURL + "redirect/" + redirect_name) + if err != nil { + return err + } + + return nil +} + +// update redirect +func (m *FirewallClient) UpdateRedirect(redirect SdewanFirewallRedirect) (*SdewanFirewallRedirect, error) { + var response string + var err error + redirect_obj, _ := json.Marshal(redirect) + redirect_name := redirect.Name + response, err = m.OpenwrtClient.Put(firewallBaseURL+"redirect/"+redirect_name, string(redirect_obj)) + if err != nil { + return nil, err + } + + var sdewanFirewallRedirect SdewanFirewallRedirect + err = json.Unmarshal([]byte(response), &sdewanFirewallRedirect) + if err != nil { + return nil, err + } + + return &sdewanFirewallRedirect, nil +} diff --git a/platform/crd-ctrlr/src/openwrt/ipsec.go b/platform/crd-ctrlr/src/openwrt/ipsec.go new file mode 100644 index 0000000..5175bd6 --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/ipsec.go @@ -0,0 +1,233 @@ +package openwrt + +import ( + "encoding/json" +) + +const ( + ipsecBaseURL = "sdewan/ipsec/v1/" +) + +type IpsecClient struct { + OpenwrtClient *openwrtClient +} + +// Proposals +type SdewanIpsecProposal struct { + Name string `json:"name"` + EncryptionAlgorithm string `json:"encryption_algorithm"` + HashAlgorithm string `json:"hash_algorithm"` + DhGroup string `json:"dh_group"` +} + +type SdewanIpsecProposals struct { + Proposals []SdewanIpsecProposal `json:"proposals"` +} + +// Sites +type SdewanIpsecConnection struct { + Name string `json:"name"` + Type string `json:"type"` + Mode string `json:"mode"` + LocalSubnet string `json:"local_subnet"` + LocalNat string `json:"local_nat"` + LocalSourceip string `json:"local_sourceip"` + LocalUpdown string `json:"local_updown"` + LocalFirewall string `json:"local_firewall"` + RemoteSubnet string `json:"remote_subnet"` + RemoteSourceip string `json:"remote_sourceip"` + RemoteUpdown string `json:"remote_updown"` + RemoteFirewall string `json:"remote_firewall"` + CryptoProposal []string `json:"crypto_proposal"` +} + +type SdewanIpsecSite struct { + Name string `json:"name"` + Gateway string `json:"gateway"` + PreSharedKey string `json:"pre_shared_key"` + AuthenticationMethod string `json:"authentication_method"` + LocalIdentifier string `json:"local_identifier"` + RemoteIdentifier string `json:"remote_identifier"` + CryptoProposal []string `json:"crypto_proposal"` + ForceCryptoProposal string `json:"force_crypto_proposal"` + LocalPublicCert string `json:"local_public_cert"` + LocalPrivateCert string `json:"local_private_cert"` + SharedCa string `json:"shared_ca"` + Connections []SdewanIpsecConnection `json:"connections"` +} + +type SdewanIpsecSites struct { + Sites []SdewanIpsecSite `json:"sites"` +} + +// Proposal APIs +// get proposals +func (f *IpsecClient) GetProposals() (*SdewanIpsecProposals, error) { + var response string + var err error + response, err = f.OpenwrtClient.Get(ipsecBaseURL + "proposals") + if err != nil { + return nil, err + } + + var sdewanIpsecProposals SdewanIpsecProposals + err = json.Unmarshal([]byte(response), &sdewanIpsecProposals) + if err != nil { + return nil, err + } + + return &sdewanIpsecProposals, nil +} + +// get proposal +func (m *IpsecClient) GetProposal(proposal string) (*SdewanIpsecProposal, error) { + var response string + var err error + response, err = m.OpenwrtClient.Get(ipsecBaseURL + "proposal/" + proposal) + if err != nil { + return nil, err + } + + var sdewanIpsecProposal SdewanIpsecProposal + err = json.Unmarshal([]byte(response), &sdewanIpsecProposal) + if err != nil { + return nil, err + } + + return &sdewanIpsecProposal, nil +} + +// create proposal +func (m *IpsecClient) CreateProposal(proposal SdewanIpsecProposal) (*SdewanIpsecProposal, error) { + var response string + var err error + proposal_obj, _ := json.Marshal(proposal) + response, err = m.OpenwrtClient.Post(ipsecBaseURL+"proposal", string(proposal_obj)) + if err != nil { + return nil, err + } + + var sdewanIpsecProposal SdewanIpsecProposal + err = json.Unmarshal([]byte(response), &sdewanIpsecProposal) + if err != nil { + return nil, err + } + + return &sdewanIpsecProposal, nil +} + +// delete proposal +func (m *IpsecClient) DeleteProposal(proposal_name string) error { + _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "proposal/" + proposal_name) + if err != nil { + return err + } + + return nil +} + +// update proposal +func (m *IpsecClient) UpdateProposal(proposal SdewanIpsecProposal) (*SdewanIpsecProposal, error) { + var response string + var err error + proposal_obj, _ := json.Marshal(proposal) + proposal_name := proposal.Name + response, err = m.OpenwrtClient.Put(ipsecBaseURL+"proposal/"+proposal_name, string(proposal_obj)) + if err != nil { + return nil, err + } + + var sdewanIpsecProposal SdewanIpsecProposal + err = json.Unmarshal([]byte(response), &sdewanIpsecProposal) + if err != nil { + return nil, err + } + + return &sdewanIpsecProposal, nil +} + +// Site APIs +// get sites +func (f *IpsecClient) GetSites() (*SdewanIpsecSites, error) { + var response string + var err error + response, err = f.OpenwrtClient.Get(ipsecBaseURL + "sites") + if err != nil { + return nil, err + } + + var sdewanIpsecSites SdewanIpsecSites + err = json.Unmarshal([]byte(response), &sdewanIpsecSites) + if err != nil { + return nil, err + } + + return &sdewanIpsecSites, nil +} + +// get site +func (m *IpsecClient) GetSite(site string) (*SdewanIpsecSite, error) { + var response string + var err error + response, err = m.OpenwrtClient.Get(ipsecBaseURL + "site/" + site) + if err != nil { + return nil, err + } + + var sdewanIpsecSite SdewanIpsecSite + err = json.Unmarshal([]byte(response), &sdewanIpsecSite) + if err != nil { + return nil, err + } + + return &sdewanIpsecSite, nil +} + +// create site +func (m *IpsecClient) CreateSite(site SdewanIpsecSite) (*SdewanIpsecSite, error) { + var response string + var err error + site_obj, _ := json.Marshal(site) + response, err = m.OpenwrtClient.Post(ipsecBaseURL+"site", string(site_obj)) + if err != nil { + return nil, err + } + + var sdewanIpsecSite SdewanIpsecSite + err = json.Unmarshal([]byte(response), &sdewanIpsecSite) + if err != nil { + return nil, err + } + + return &sdewanIpsecSite, nil +} + +// delete site +func (m *IpsecClient) DeleteSite(site_name string) error { + _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "site/" + site_name) + if err != nil { + return err + } + + return nil +} + +// update site +func (m *IpsecClient) UpdateSite(site SdewanIpsecSite) (*SdewanIpsecSite, error) { + var response string + var err error + site_obj, _ := json.Marshal(site) + site_name := site.Name + response, err = m.OpenwrtClient.Put(ipsecBaseURL+"site/"+site_name, string(site_obj)) + if err != nil { + return nil, err + } + + var sdewanIpsecSite SdewanIpsecSite + err = json.Unmarshal([]byte(response), &sdewanIpsecSite) + if err != nil { + return nil, err + } + + return &sdewanIpsecSite, nil +} diff --git a/platform/crd-ctrlr/src/openwrt/mwan3.go b/platform/crd-ctrlr/src/openwrt/mwan3.go new file mode 100644 index 0000000..8d6c6a2 --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/mwan3.go @@ -0,0 +1,242 @@ +package openwrt + +import ( + "encoding/json" +) + +const ( + mwan3BaseURL = "sdewan/mwan3/v1/" +) + +type Mwan3Client struct { + OpenwrtClient *openwrtClient +} + +// MWAN3 interface status +type IpStatus struct { + Status string `json:"status"` + Latency int `json:"latency"` + Packetloss int `json:"packetloss"` + Ip string `json:"ip"` +} + +type WanInterfaceStatus struct { + Running bool `json:"running"` + Score int `json:"score"` + Lost int `json:"lost"` + Status string `json:"status"` + Age int `json:"age"` + Turn int `json:"turn"` + Ips []IpStatus `json:"track_ip"` +} + +type InterfaceStatus struct { + Interfaces map[string]WanInterfaceStatus `json:"interfaces"` + Connected map[string][]string `json:"connected"` +} + +// MWAN3 Policy +type SdewanMember struct { + Interface string `json:"interface"` + Metric string `json:"metric"` + Weight string `json:"weight"` +} + +type SdewanPolicy struct { + Name string `json:"name"` + Members []SdewanMember `json:"members"` +} + +type SdewanPolicies struct { + Policies []SdewanPolicy `json:"policies"` +} + +// MWAN3 Rule +type SdewanRule struct { + Name string `json:"name"` + Policy string `json:"policy"` + SrcIp string `json:"src_ip"` + SrcPort string `json:"src_port"` + DestIp string `json:"dest_ip"` + DestPort string `json:"dest_port"` + Proto string `json:"proto"` + Family string `json:"family"` + Sticky string `json:"sticky"` + Timeout string `json:"timeout"` +} + +type SdewanRules struct { + Rules []SdewanRule `json:"rules"` +} + +// get interface status +func (m *Mwan3Client) GetInterfaceStatus() (*InterfaceStatus, error) { + response, err := m.OpenwrtClient.Get("admin/status/mwan/interface_status") + if err != nil { + return nil, err + } + + var interfaceStatus InterfaceStatus + err2 := json.Unmarshal([]byte(response), &interfaceStatus) + if err2 != nil { + return nil, err2 + } + + return &interfaceStatus, nil +} + +// Policy APIs +// get policies +func (m *Mwan3Client) GetPolicies() (*SdewanPolicies, error) { + response, err := m.OpenwrtClient.Get(mwan3BaseURL + "policies") + if err != nil { + return nil, err + } + + var sdewanPolicies SdewanPolicies + err2 := json.Unmarshal([]byte(response), &sdewanPolicies) + if err2 != nil { + return nil, err2 + } + + return &sdewanPolicies, nil +} + +// get policy +func (m *Mwan3Client) GetPolicy(policy_name string) (*SdewanPolicy, error) { + response, err := m.OpenwrtClient.Get(mwan3BaseURL + "policies/" + policy_name) + if err != nil { + return nil, err + } + + var sdewanPolicy SdewanPolicy + err2 := json.Unmarshal([]byte(response), &sdewanPolicy) + if err2 != nil { + return nil, err2 + } + + return &sdewanPolicy, nil +} + +// create policy +func (m *Mwan3Client) CreatePolicy(policy SdewanPolicy) (*SdewanPolicy, error) { + policy_obj, _ := json.Marshal(policy) + response, err := m.OpenwrtClient.Post(mwan3BaseURL+"policies", string(policy_obj)) + if err != nil { + return nil, err + } + + var sdewanPolicy SdewanPolicy + err2 := json.Unmarshal([]byte(response), &sdewanPolicy) + if err2 != nil { + return nil, err2 + } + + return &sdewanPolicy, nil +} + +// delete policy +func (m *Mwan3Client) DeletePolicy(policy_name string) error { + _, err := m.OpenwrtClient.Delete(mwan3BaseURL + "policies/" + policy_name) + if err != nil { + return err + } + + return nil +} + +// update policy +func (m *Mwan3Client) UpdatePolicy(policy SdewanPolicy) (*SdewanPolicy, error) { + policy_obj, _ := json.Marshal(policy) + policy_name := policy.Name + response, err := m.OpenwrtClient.Put(mwan3BaseURL+"policies/"+policy_name, string(policy_obj)) + if err != nil { + return nil, err + } + + var sdewanPolicy SdewanPolicy + err2 := json.Unmarshal([]byte(response), &sdewanPolicy) + if err2 != nil { + return nil, err2 + } + + return &sdewanPolicy, nil +} + +// Rule APIs +// get rules +func (m *Mwan3Client) GetRules() (*SdewanRules, error) { + response, err := m.OpenwrtClient.Get(mwan3BaseURL + "rules") + if err != nil { + return nil, err + } + + var sdewanRules SdewanRules + err2 := json.Unmarshal([]byte(response), &sdewanRules) + if err2 != nil { + return nil, err2 + } + + return &sdewanRules, nil +} + +// get rule +func (m *Mwan3Client) GetRule(rule string) (*SdewanRule, error) { + response, err := m.OpenwrtClient.Get(mwan3BaseURL + "rules/" + rule) + if err != nil { + return nil, err + } + + var sdewanRule SdewanRule + err2 := json.Unmarshal([]byte(response), &sdewanRule) + if err2 != nil { + return nil, err2 + } + + return &sdewanRule, nil +} + +// create rule +func (m *Mwan3Client) CreateRule(rule SdewanRule) (*SdewanRule, error) { + rule_obj, _ := json.Marshal(rule) + response, err := m.OpenwrtClient.Post(mwan3BaseURL+"rules", string(rule_obj)) + if err != nil { + return nil, err + } + + var sdewanRule SdewanRule + err2 := json.Unmarshal([]byte(response), &sdewanRule) + if err2 != nil { + return nil, err2 + } + + return &sdewanRule, nil +} + +// delete rule +func (m *Mwan3Client) DeleteRule(rule_name string) error { + _, err := m.OpenwrtClient.Delete(mwan3BaseURL + "rules/" + rule_name) + if err != nil { + return err + } + + return nil +} + +// update rule +func (m *Mwan3Client) UpdateRule(rule SdewanRule) (*SdewanRule, error) { + rule_obj, _ := json.Marshal(rule) + rule_name := rule.Name + response, err := m.OpenwrtClient.Put(mwan3BaseURL+"rules/"+rule_name, string(rule_obj)) + if err != nil { + return nil, err + } + + var sdewanRule SdewanRule + err2 := json.Unmarshal([]byte(response), &sdewanRule) + if err2 != nil { + return nil, err2 + } + + return &sdewanRule, nil +} diff --git a/platform/crd-ctrlr/src/openwrt/openwrtclient.go b/platform/crd-ctrlr/src/openwrt/openwrtclient.go new file mode 100644 index 0000000..2d0dee6 --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/openwrtclient.go @@ -0,0 +1,166 @@ +package openwrt + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "runtime" + "strings" +) + +type OpenwrtError struct { + Code int + Message string +} + +func (e *OpenwrtError) Error() string { + return fmt.Sprintf("Error Code: %d, Error Message: %s", e.Code, e.Message) +} + +type openwrtClient struct { + ip string + user string + password string + token string +} + +func CloseClient(o *openwrtClient) { + o.logout() + runtime.SetFinalizer(o, nil) +} + +func NewOpenwrtClient(ip string, user string, password string) *openwrtClient { + client := &openwrtClient{ + ip: ip, + user: user, + password: password, + token: "", + } + + runtime.SetFinalizer(client, CloseClient) + return client +} + +// openwrt base URL +func (o *openwrtClient) getBaseURL() string { + return "http://" + o.ip + "/cgi-bin/luci/" +} + +// login to openwrt http server +func (o *openwrtClient) login() error { + client := &http.Client{ + // block redirect + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + + // login + login_info := "luci_username=" + o.user + "&luci_password=" + o.password + var req_body = []byte(login_info) + req, _ := http.NewRequest("POST", o.getBaseURL(), bytes.NewBuffer(req_body)) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + resp, err := client.Do(req) + if resp != nil { + defer resp.Body.Close() + } + + if err != nil { + return err + } else if resp.StatusCode != 302 { + // fail to auth + return &OpenwrtError{Code: resp.StatusCode, Message: "Unauthorized"} + } else { + // get token + res_cookie := resp.Header["Set-Cookie"][0] + res_cookies := strings.Split(res_cookie, ";") + for _, cookie := range res_cookies { + cookie := strings.TrimSpace(cookie) + index := strings.Index(cookie, "=") + var key = cookie + var value = "" + if index != -1 { + key = cookie[:index] + value = cookie[index+1:] + } + + if key == "sysauth" { + o.token = value + break + } + } + } + + return nil +} + +// logout to openwrt http server +func (o *openwrtClient) logout() error { + if o.token != "" { + _, err := o.Get("admin/logout") + o.token = "" + return err + } + + return nil +} + +// call openwrt restful API +func (o *openwrtClient) call(method string, url string, request string) (string, error) { + for i := 0; i < 2; i++ { + if o.token == "" { + err := o.login() + if err != nil { + return "", err + } + } + + client := &http.Client{} + req_body := bytes.NewBuffer([]byte(request)) + req, _ := http.NewRequest(method, o.getBaseURL()+url, req_body) + req.Header.Add("Cookie", "sysauth="+o.token) + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, _ := ioutil.ReadAll(resp.Body) + if resp.StatusCode >= 400 { + if resp.StatusCode == 403 { + // token expired, retry + o.token = "" + continue + } else { + // error request + return "", &OpenwrtError{Code: resp.StatusCode, Message: string(body)} + } + } + + return string(body), nil + } + + return "", nil +} + +// call openwrt Get restful API +func (o *openwrtClient) Get(url string) (string, error) { + return o.call("GET", url, "") +} + +// call openwrt restful API +func (o *openwrtClient) Post(url string, request string) (string, error) { + return o.call("POST", url, request) +} + +// call openwrt restful API +func (o *openwrtClient) Put(url string, request string) (string, error) { + return o.call("PUT", url, request) +} + +// call openwrt restful API +func (o *openwrtClient) Delete(url string) (string, error) { + return o.call("DELETE", url, "") +} diff --git a/platform/crd-ctrlr/src/openwrt/service.go b/platform/crd-ctrlr/src/openwrt/service.go new file mode 100644 index 0000000..7580e94 --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/service.go @@ -0,0 +1,54 @@ +package openwrt + +import ( + "encoding/json" +) + +const ( + serviceBaseURL = "sdewan/v1/" +) + +var available_Services = []string{"mwan3", "firewall", "ipsec"} + +type ServiceClient struct { + OpenwrtClient *openwrtClient +} + +// Service API struct +type AvailableServices struct { + Services []string `json:"services"` +} + +// get available services +func (s *ServiceClient) GetAvailableServices() (*AvailableServices, error) { + response, err := s.OpenwrtClient.Get(serviceBaseURL + "services") + if err != nil { + return nil, err + } + + var servs AvailableServices + err2 := json.Unmarshal([]byte(response), &servs) + if err2 != nil { + return nil, err2 + } + + return &servs, nil +} + +func (s *ServiceClient) formatExecuteServiceBody(operation string) string { + return "{\"action\":\"" + operation + "\"}" +} + +// execute operation on service +func (s *ServiceClient) ExecuteService(service string, operation string) (bool, error) { + if !IsContained(available_Services, service) { + return false, &OpenwrtError{Code: 400, Message: "Bad Request: not supported service(" + service + ")"} + } + + _, err := s.OpenwrtClient.Put(serviceBaseURL+"service/"+service, s.formatExecuteServiceBody(operation)) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/platform/crd-ctrlr/src/openwrt/utils.go b/platform/crd-ctrlr/src/openwrt/utils.go new file mode 100644 index 0000000..c37631d --- /dev/null +++ b/platform/crd-ctrlr/src/openwrt/utils.go @@ -0,0 +1,22 @@ +package openwrt + +import ( + "reflect" +) + +// util function to check whether items contains item +func IsContained(items interface{}, item interface{}) bool { + switch reflect.TypeOf(items).Kind() { + case reflect.Slice: + v := reflect.ValueOf(items) + for i := 0; i < v.Len(); i++ { + if reflect.DeepEqual(item, v.Index(i).Interface()) { + return true + } + } + default: + return false + } + + return false +} -- 2.16.6