- 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.
+- We have extracted the common logics of controllers, and implemeted the second crd/controller with it
### What we don't have yet
- Add raw webhook to implemente the label based permission system
- Add validation webhook to validate CR
+### NOTEs
+
+- We need controller-runtime version at least v0.6.0 to support `GenerationChangedPredicate` which is used to prevent CR status update trigering reconcile
+
## References
- https://book.kubebuilder.io/
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.2.4
+ controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: mwan3policies.batch.sdewan.akraino.org
spec:
status:
description: status subsource used for Sdewan rule CRDs
properties:
+ appliedGeneration:
+ format: int64
+ type: integer
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'
+ message:
+ type: string
+ state:
type: string
- inSync:
- type: boolean
required:
- - appliedTime
- - appliedVersion
- - inSync
+ - state
+ type: object
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.2.5
+ creationTimestamp: null
+ name: mwan3rules.batch.sdewan.akraino.org
+spec:
+ group: batch.sdewan.akraino.org
+ names:
+ kind: Mwan3Rule
+ listKind: Mwan3RuleList
+ plural: mwan3rules
+ singular: mwan3rule
+ scope: Namespaced
+ subresources:
+ status: {}
+ validation:
+ openAPIV3Schema:
+ description: Mwan3Rule is the Schema for the mwan3rules 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:
+ dest_ip:
+ type: string
+ dest_port:
+ type: string
+ family:
+ type: string
+ policy:
+ description: Members []Mwan3Rule `json:"members"`
+ type: string
+ proto:
+ type: string
+ src_ip:
+ type: string
+ src_port:
+ type: string
+ sticky:
+ type: string
+ timeout:
+ type: string
+ required:
+ - dest_ip
+ - dest_port
+ - family
+ - policy
+ - proto
+ - src_ip
+ - src_port
+ - sticky
+ - timeout
+ type: object
+ status:
+ description: status subsource used for Sdewan rule CRDs
+ properties:
+ appliedGeneration:
+ format: int64
+ type: integer
+ appliedTime:
+ format: date-time
+ type: string
+ message:
+ type: string
+ state:
+ type: string
+ required:
+ - state
type: object
type: object
version: v1alpha1
creationTimestamp: null
name: sdewan-manager-role
rules:
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments/status
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- batch.sdewan.akraino.org
resources:
- patch
- update
- apiGroups:
- - extensions
+ - batch.sdewan.akraino.org
resources:
- - deployments
+ - mwan3rules
verbs:
+ - create
+ - delete
- get
- list
+ - patch
+ - update
- watch
+- apiGroups:
+ - batch.sdewan.akraino.org
+ resources:
+ - mwan3rules/status
+ verbs:
+ - get
+ - patch
+ - update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/
+COPY basehandler/ basehandler/
COPY cnfprovider/ cnfprovider/
COPY openwrt/ openwrt/
# Build the docker image
docker-build: test
- docker build . -t ${IMG}
+ docker build --network=host . -t ${IMG}
# Push the docker image
docker-push:
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 ;\
+ go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
}
CONTROLLER_GEN=$(GOBIN)/controller-gen
- group: batch
kind: Mwan3Policy
version: v1alpha1
+- group: batch
+ kind: Mwan3Rule
+ version: v1alpha1
version: "2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
+type SdewanState string
+
+const (
+ InSync SdewanState = "In Sync"
+ Idle = "Idle"
+ Applying = "Trying to apply"
+ Deleting = "Being delete"
+ Unknown = "Unknown status"
+)
+
// 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"`
+ // +optional
+ AppliedGeneration int64 `json:"appliedGeneration,omitempty"`
+ // +optional
+ AppliedTime *metav1.Time `json:"appliedTime,omitempty"`
+ State SdewanState `json:"state"`
+ // +optional
+ Message string `json:"message,omitempty"`
}
--- /dev/null
+/*
+
+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.
+
+// Mwan3RuleSpec defines the desired state of Mwan3Rule
+
+type Mwan3RuleSpec struct {
+ 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"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// Mwan3Rule is the Schema for the mwan3rules API
+type Mwan3Rule struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec Mwan3RuleSpec `json:"spec,omitempty"`
+ Status SdewanStatus `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// Mwan3RuleList contains a list of Mwan3Rule
+type Mwan3RuleList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []Mwan3Rule `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&Mwan3Rule{}, &Mwan3RuleList{})
+}
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Mwan3Rule) DeepCopyInto(out *Mwan3Rule) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3Rule.
+func (in *Mwan3Rule) DeepCopy() *Mwan3Rule {
+ if in == nil {
+ return nil
+ }
+ out := new(Mwan3Rule)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Mwan3Rule) 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 *Mwan3RuleList) DeepCopyInto(out *Mwan3RuleList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]Mwan3Rule, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3RuleList.
+func (in *Mwan3RuleList) DeepCopy() *Mwan3RuleList {
+ if in == nil {
+ return nil
+ }
+ out := new(Mwan3RuleList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Mwan3RuleList) 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 *Mwan3RuleSpec) DeepCopyInto(out *Mwan3RuleSpec) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mwan3RuleSpec.
+func (in *Mwan3RuleSpec) DeepCopy() *Mwan3RuleSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(Mwan3RuleSpec)
+ 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
--- /dev/null
+package basehandler
+
+import (
+ "context"
+ appsv1 "k8s.io/api/apps/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "sdewan.akraino.org/sdewan/openwrt"
+)
+
+type ISdewanHandler interface {
+ GetType() string
+ GetName(instance runtime.Object) string
+ GetFinalizer() string
+ GetInstance(r client.Client, ctx context.Context, req ctrl.Request) (runtime.Object, error)
+ Convert(o runtime.Object, deployment appsv1.Deployment) (openwrt.IOpenWrtObject, error)
+ IsEqual(instance1 openwrt.IOpenWrtObject, instance2 openwrt.IOpenWrtObject) bool
+ GetObject(clientInfo *openwrt.OpenwrtClientInfo, name string) (openwrt.IOpenWrtObject, error)
+ CreateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error)
+ UpdateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error)
+ DeleteObject(clientInfo *openwrt.OpenwrtClientInfo, name string) error
+ Restart(clientInfo *openwrt.OpenwrtClientInfo) (bool, error)
+}
+++ /dev/null
-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)
-}
--- /dev/null
+package cnfprovider
+
+import (
+ "context"
+ "errors"
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ basehandler "sdewan.akraino.org/sdewan/basehandler"
+ "sdewan.akraino.org/sdewan/openwrt"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+)
+
+var log = logf.Log.WithName("OpenWrtProvider")
+
+type OpenWrtProvider struct {
+ Namespace string
+ SdewanPurpose string
+ Deployment appsv1.Deployment
+ K8sClient client.Client
+}
+
+func NewOpenWrt(namespace string, sdewanPurpose string, k8sClient client.Client) (*OpenWrtProvider, error) {
+ ctx := context.Background()
+ deployments := &appsv1.DeploymentList{}
+ err := k8sClient.List(ctx, deployments, client.MatchingLabels{"sdewanPurpose": sdewanPurpose})
+ if err != nil {
+ return nil, client.IgnoreNotFound(err)
+ }
+ if len(deployments.Items) > 1 {
+ return nil, errors.New("More than one deployment exists")
+ }
+ if len(deployments.Items) < 1 {
+ // return (nil, nil) to indicate that no cnf exists
+ return nil, nil
+ }
+ return &OpenWrtProvider{namespace, sdewanPurpose, deployments.Items[0], k8sClient}, nil
+}
+
+func (p *OpenWrtProvider) AddOrUpdateObject(handler basehandler.ISdewanHandler, instance runtime.Object) (bool, error) {
+ reqLogger := log.WithValues(handler.GetType(), handler.GetName(instance), "cnf", p.Deployment.Name)
+ ctx := context.Background()
+ podList := &corev1.PodList{}
+ err := p.K8sClient.List(ctx, podList, client.MatchingLabels{"sdewanPurpose": p.SdewanPurpose})
+ if err != nil {
+ return false, err
+ }
+ new_instance, err := handler.Convert(instance, p.Deployment)
+ if err != nil {
+ return false, err
+ }
+ cnfChanged := false
+ for _, pod := range podList.Items {
+ clientInfo := &openwrt.OpenwrtClientInfo{Ip: pod.Status.PodIP, User: "root", Password: ""}
+ runtime_instance, err := handler.GetObject(clientInfo, new_instance.GetName())
+ changed := false
+
+ if err != nil {
+ _, err := handler.CreateObject(clientInfo, new_instance)
+ if err != nil {
+ return false, err
+ }
+ changed = true
+ } else if handler.IsEqual(runtime_instance, new_instance) {
+ reqLogger.Info("Equal to the runtime instance, so no update")
+ } else {
+ _, err := handler.UpdateObject(clientInfo, new_instance)
+ if err != nil {
+ return false, err
+ }
+ changed = true
+ }
+ if changed {
+ _, err = handler.Restart(clientInfo)
+ if err != nil {
+ return changed, err
+ }
+ cnfChanged = true
+ }
+ }
+ // We say the AddUpdate succeed only when the add/update for all pods succeed
+ return cnfChanged, nil
+}
+
+func (p *OpenWrtProvider) DeleteObject(handler basehandler.ISdewanHandler, instance runtime.Object) (bool, error) {
+ reqLogger := log.WithValues(handler.GetType(), handler.GetName(instance), "cnf", p.Deployment.Name)
+ ctx := context.Background()
+ podList := &corev1.PodList{}
+ err := p.K8sClient.List(ctx, podList, client.MatchingLabels{"sdewanPurpose": p.SdewanPurpose})
+ if err != nil {
+ return false, err
+ }
+ cnfChanged := false
+ for _, pod := range podList.Items {
+ clientInfo := &openwrt.OpenwrtClientInfo{Ip: pod.Status.PodIP, User: "root", Password: ""}
+ runtime_instance, _ := handler.GetObject(clientInfo, handler.GetName(instance))
+ if runtime_instance == nil {
+ reqLogger.Info("Runtime instance doesn't exist, so don't have to delete")
+ } else {
+ err = handler.DeleteObject(clientInfo, handler.GetName(instance))
+ if err != nil {
+ return false, err
+ }
+ _, err = handler.Restart(clientInfo)
+ if err != nil {
+ return false, err
+ }
+ cnfChanged = true
+ }
+ }
+ // We say the deletioni succeed only when the deletion for all pods succeed
+ return cnfChanged, nil
+}
+++ /dev/null
-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
-}
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.2.4
+ controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: mwan3policies.batch.sdewan.akraino.org
spec:
status:
description: status subsource used for Sdewan rule CRDs
properties:
+ appliedGeneration:
+ format: int64
+ type: integer
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'
+ message:
+ type: string
+ state:
type: string
- inSync:
- type: boolean
required:
- - appliedTime
- - appliedVersion
- - inSync
+ - state
type: object
type: object
version: v1alpha1
--- /dev/null
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.2.5
+ creationTimestamp: null
+ name: mwan3rules.batch.sdewan.akraino.org
+spec:
+ group: batch.sdewan.akraino.org
+ names:
+ kind: Mwan3Rule
+ listKind: Mwan3RuleList
+ plural: mwan3rules
+ singular: mwan3rule
+ scope: Namespaced
+ subresources:
+ status: {}
+ validation:
+ openAPIV3Schema:
+ description: Mwan3Rule is the Schema for the mwan3rules 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:
+ dest_ip:
+ type: string
+ dest_port:
+ type: string
+ family:
+ type: string
+ policy:
+ description: Members []Mwan3Rule `json:"members"`
+ type: string
+ proto:
+ type: string
+ src_ip:
+ type: string
+ src_port:
+ type: string
+ sticky:
+ type: string
+ timeout:
+ type: string
+ required:
+ - dest_ip
+ - dest_port
+ - family
+ - policy
+ - proto
+ - src_ip
+ - src_port
+ - sticky
+ - timeout
+ type: object
+ status:
+ description: status subsource used for Sdewan rule CRDs
+ properties:
+ appliedGeneration:
+ format: int64
+ type: integer
+ appliedTime:
+ format: date-time
+ type: string
+ message:
+ type: string
+ state:
+ type: string
+ required:
+ - state
+ type: object
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
# It should be run by config/default
resources:
- bases/batch.sdewan.akraino.org_mwan3policies.yaml
+- bases/batch.sdewan.akraino.org_mwan3rules.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
+#- patches/webhook_in_mwan3rules.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
+#- patches/cainjection_in_mwan3rules.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
--- /dev/null
+# 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: mwan3rules.batch.sdewan.akraino.org
--- /dev/null
+# 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: mwan3rules.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
+++ /dev/null
-# 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
+++ /dev/null
-# 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
creationTimestamp: null
name: manager-role
rules:
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments/status
+ verbs:
+ - get
+ - list
+ - watch
- apiGroups:
- batch.sdewan.akraino.org
resources:
- patch
- update
- apiGroups:
- - extensions
+ - batch.sdewan.akraino.org
resources:
- - deployments
+ - mwan3rules
verbs:
+ - create
+ - delete
- get
- list
+ - patch
+ - update
- watch
+- apiGroups:
+ - batch.sdewan.akraino.org
+ resources:
+ - mwan3rules/status
+ verbs:
+ - get
+ - patch
+ - update
spec:
members:
- network: ovn-net1
- weight: 4
+ weight: 2
metric: 2
- network: ovn-net2
weight: 3
--- /dev/null
+apiVersion: batch.sdewan.akraino.org/v1alpha1
+kind: Mwan3Rule
+metadata:
+ name: sample
+ namespace: default
+ labels:
+ sdewanPurpose: cnf1
+spec:
+ # Add fields here
+ dest_ip: "10.10.10.1"
+ dest_port: "1000"
+ family: ipv4
+ policy: balance1
+ proto: udp
+ src_ip: "10.10.10.10"
+ src_port: "22"
+ sticky: "1"
+ timeout: "200"
+
--- /dev/null
+package controllers
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/go-logr/logr"
+ errs "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "reflect"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "time"
+
+ appsv1 "k8s.io/api/apps/v1"
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
+ "sdewan.akraino.org/sdewan/basehandler"
+ "sdewan.akraino.org/sdewan/cnfprovider"
+)
+
+// 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
+}
+
+func getPurpose(instance runtime.Object) string {
+ value := reflect.ValueOf(instance)
+ field := reflect.Indirect(value).FieldByName("Labels")
+ labels := field.Interface().(map[string]string)
+ return labels["sdewanPurpose"]
+}
+
+func getDeletionTempstamp(instance runtime.Object) *metav1.Time {
+ value := reflect.ValueOf(instance)
+ field := reflect.Indirect(value).FieldByName("DeletionTimestamp")
+ return field.Interface().(*metav1.Time)
+}
+
+func getFinalizers(instance runtime.Object) []string {
+ value := reflect.ValueOf(instance)
+ field := reflect.Indirect(value).FieldByName("Finalizers")
+ return field.Interface().([]string)
+}
+
+func setStatus(instance runtime.Object, status batchv1alpha1.SdewanStatus) {
+ value := reflect.ValueOf(instance)
+ field_status := reflect.Indirect(value).FieldByName("Status")
+ if status.State == batchv1alpha1.InSync {
+ field_gv := reflect.Indirect(value).FieldByName("Generation")
+ status.AppliedGeneration = field_gv.Interface().(int64)
+ status.AppliedTime = &metav1.Time{Time: time.Now()}
+ status.Message = ""
+ } else {
+ status.AppliedGeneration = 0
+ status.AppliedTime = nil
+ }
+ field_status.Set(reflect.ValueOf(status))
+}
+
+func appendFinalizer(instance runtime.Object, item string) {
+ value := reflect.ValueOf(instance)
+ field := reflect.Indirect(value).FieldByName("ObjectMeta")
+ base_obj := field.Interface().(metav1.ObjectMeta)
+ base_obj.Finalizers = append(base_obj.Finalizers, item)
+ field.Set(reflect.ValueOf(base_obj))
+}
+
+func removeFinalizer(instance runtime.Object, item string) {
+ value := reflect.ValueOf(instance)
+ field := reflect.Indirect(value).FieldByName("ObjectMeta")
+ base_obj := field.Interface().(metav1.ObjectMeta)
+ base_obj.Finalizers = removeString(base_obj.Finalizers, item)
+ field.Set(reflect.ValueOf(base_obj))
+}
+
+func net2iface(net string, deployment appsv1.Deployment) (string, error) {
+ type Iface struct {
+ DefaultGateway bool `json:"defaultGateway,string"`
+ Interface string
+ Name string
+ }
+ type NfnNet struct {
+ Type string
+ Interface []Iface
+ }
+ ann := 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))
+}
+
+// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch
+// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;list;watch
+
+// Common Reconcile Processing
+func ProcessReconcile(r client.Client, logger logr.Logger, req ctrl.Request, handler basehandler.ISdewanHandler) (ctrl.Result, error) {
+ ctx := context.Background()
+ log := logger.WithValues(handler.GetType(), req.NamespacedName)
+ during, _ := time.ParseDuration("5s")
+
+ instance, err := handler.GetInstance(r, ctx, req)
+ if err != nil {
+ if errs.IsNotFound(err) {
+ // No instance
+ return ctrl.Result{}, nil
+ }
+ // Error reading the object - requeue the request.
+ return ctrl.Result{RequeueAfter: during}, nil
+ }
+ purpose := getPurpose(instance)
+ cnf, err := cnfprovider.NewOpenWrt(req.NamespacedName.Namespace, purpose, r)
+ if err != nil {
+ log.Error(err, "Failed to get cnf")
+ setStatus(instance, batchv1alpha1.SdewanStatus{State: batchv1alpha1.Unknown, Message: err.Error()})
+ err = r.Status().Update(ctx, instance)
+ if err != nil {
+ log.Error(err, "Failed to update status for "+handler.GetType())
+ return ctrl.Result{}, err
+ }
+ // A new event are supposed to be received upon cnf ready
+ // so not requeue
+ return ctrl.Result{}, nil
+ }
+ finalizerName := handler.GetFinalizer()
+ delete_timestamp := getDeletionTempstamp(instance)
+
+ if delete_timestamp.IsZero() {
+ // creating or updating CR
+ if cnf == nil {
+ // no cnf exists
+ log.Info("No cnf exist, so not create/update " + handler.GetType())
+ return ctrl.Result{}, nil
+ }
+ changed, err := cnf.AddOrUpdateObject(handler, instance)
+ if err != nil {
+ log.Error(err, "Failed to add/update "+handler.GetType())
+ setStatus(instance, batchv1alpha1.SdewanStatus{State: batchv1alpha1.Applying, Message: err.Error()})
+ err = r.Status().Update(ctx, instance)
+ if err != nil {
+ log.Error(err, "Failed to update status for "+handler.GetType())
+ return ctrl.Result{}, err
+ }
+ return ctrl.Result{RequeueAfter: during}, nil
+ }
+ finalizers := getFinalizers(instance)
+ if !containsString(finalizers, finalizerName) {
+ log.Info("Adding finalizer for " + handler.GetType())
+ appendFinalizer(instance, finalizerName)
+ if err := r.Update(ctx, instance); err != nil {
+ return ctrl.Result{}, err
+ }
+ }
+ if changed {
+ setStatus(instance, batchv1alpha1.SdewanStatus{State: batchv1alpha1.InSync})
+
+ err = r.Status().Update(ctx, instance)
+ if err != nil {
+ log.Error(err, "Failed to update status for "+handler.GetType())
+ return ctrl.Result{}, err
+ }
+ }
+ } else {
+ // deletin CR
+ if cnf == nil {
+ // no cnf exists
+ finalizers := getFinalizers(instance)
+ if containsString(finalizers, finalizerName) {
+ // instance.ObjectMeta.Finalizers = removeString(instance.ObjectMeta.Finalizers, finalizerName)
+ removeFinalizer(instance, finalizerName)
+ if err := r.Update(ctx, instance); err != nil {
+ return ctrl.Result{}, err
+ }
+ }
+ return ctrl.Result{}, nil
+ }
+ _, err := cnf.DeleteObject(handler, instance)
+
+ if err != nil {
+ log.Error(err, "Failed to delete "+handler.GetType())
+ setStatus(instance, batchv1alpha1.SdewanStatus{State: batchv1alpha1.Deleting, Message: err.Error()})
+ err = r.Status().Update(ctx, instance)
+ if err != nil {
+ log.Error(err, "Failed to update status for "+handler.GetType())
+ return ctrl.Result{}, err
+ }
+ return ctrl.Result{RequeueAfter: during}, nil
+ }
+ finalizers := getFinalizers(instance)
+ if containsString(finalizers, finalizerName) {
+ removeFinalizer(instance, finalizerName)
+ if err := r.Update(ctx, instance); err != nil {
+ return ctrl.Result{}, err
+ }
+ }
+ }
+
+ return ctrl.Result{}, nil
+}
import (
"context"
- "time"
+ "reflect"
+ "strconv"
"github.com/go-logr/logr"
- "k8s.io/apimachinery/pkg/api/errors"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
- "sdewan.akraino.org/sdewan/cnfprovider"
+ "sdewan.akraino.org/sdewan/openwrt"
)
-// Mwan3PolicyReconciler reconciles a Mwan3Policy object
-type Mwan3PolicyReconciler struct {
- client.Client
- Log logr.Logger
- Scheme *runtime.Scheme
+var mwan3PolicyHandler = new(Mwan3PolicyHandler)
+
+type Mwan3PolicyHandler struct {
}
-// +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 (m *Mwan3PolicyHandler) GetType() string {
+ return "Mwan3Policy"
+}
-func (r *Mwan3PolicyReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
- ctx := context.Background()
- log := r.Log.WithValues("mwan3policy", req.NamespacedName)
+func (m *Mwan3PolicyHandler) GetName(instance runtime.Object) string {
+ policy := instance.(*batchv1alpha1.Mwan3Policy)
+ return policy.Name
+}
- // your logic here
- during, _ := time.ParseDuration("5s")
+func (m *Mwan3PolicyHandler) GetFinalizer() string {
+ return "rule.finalizers.sdewan.akraino.org"
+}
+
+func (m *Mwan3PolicyHandler) GetInstance(r client.Client, ctx context.Context, req ctrl.Request) (runtime.Object, error) {
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)
+ return instance, err
+}
+
+func (m *Mwan3PolicyHandler) Convert(instance runtime.Object, deployment appsv1.Deployment) (openwrt.IOpenWrtObject, error) {
+ policy := instance.(*batchv1alpha1.Mwan3Policy)
+ members := make([]openwrt.SdewanMember, len(policy.Spec.Members))
+ for i, membercr := range policy.Spec.Members {
+ iface, err := net2iface(membercr.Network, deployment)
if err != nil {
- log.Error(err, "Failed to delete mwan3 policy")
- return ctrl.Result{RequeueAfter: during}, nil
+ return nil, err
}
- 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
- }
+ members[i] = openwrt.SdewanMember{
+ Interface: iface,
+ Metric: strconv.Itoa(membercr.Metric),
+ Weight: strconv.Itoa(membercr.Weight),
}
}
+ return &openwrt.SdewanPolicy{Name: policy.Name, Members: members}, nil
+}
- return ctrl.Result{}, nil
+func (m *Mwan3PolicyHandler) IsEqual(instance1 openwrt.IOpenWrtObject, instance2 openwrt.IOpenWrtObject) bool {
+ policy1 := instance1.(*openwrt.SdewanPolicy)
+ policy2 := instance2.(*openwrt.SdewanPolicy)
+ return reflect.DeepEqual(*policy1, *policy2)
}
-func (r *Mwan3PolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
- return ctrl.NewControllerManagedBy(mgr).
- For(&batchv1alpha1.Mwan3Policy{}).
- Complete(r)
+func (m *Mwan3PolicyHandler) GetObject(clientInfo *openwrt.OpenwrtClientInfo, name string) (openwrt.IOpenWrtObject, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ ret, err := mwan3.GetPolicy(name)
+ return ret, err
}
-// 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 (m *Mwan3PolicyHandler) CreateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ policy := instance.(*openwrt.SdewanPolicy)
+ return mwan3.CreatePolicy(*policy)
}
-func removeString(slice []string, s string) (result []string) {
- for _, item := range slice {
- if item == s {
- continue
- }
- result = append(result, item)
- }
- return
+func (m *Mwan3PolicyHandler) UpdateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ policy := instance.(*openwrt.SdewanPolicy)
+ return mwan3.UpdatePolicy(*policy)
+}
+
+func (m *Mwan3PolicyHandler) DeleteObject(clientInfo *openwrt.OpenwrtClientInfo, name string) error {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ return mwan3.DeletePolicy(name)
+}
+
+func (m *Mwan3PolicyHandler) Restart(clientInfo *openwrt.OpenwrtClientInfo) (bool, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ service := openwrt.ServiceClient{OpenwrtClient: openwrtClient}
+ return service.ExecuteService("mwan3", "restart")
+}
+
+// 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
+
+func (r *Mwan3PolicyReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
+ return ProcessReconcile(r, r.Log, req, mwan3PolicyHandler)
+}
+
+func (r *Mwan3PolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ ps := builder.WithPredicates(predicate.GenerationChangedPredicate{})
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&batchv1alpha1.Mwan3Policy{}, ps).
+ Complete(r)
}
--- /dev/null
+/*
+
+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"
+ "reflect"
+
+ "github.com/go-logr/logr"
+ appsv1 "k8s.io/api/apps/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/builder"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
+ "sdewan.akraino.org/sdewan/openwrt"
+)
+
+var mwan3RuleHandler = new(Mwan3RuleHandler)
+
+type Mwan3RuleHandler struct {
+}
+
+func (m *Mwan3RuleHandler) GetType() string {
+ return "Mwan3Rule"
+}
+
+func (m *Mwan3RuleHandler) GetName(instance runtime.Object) string {
+ Rule := instance.(*batchv1alpha1.Mwan3Rule)
+ return Rule.Name
+}
+
+func (m *Mwan3RuleHandler) GetFinalizer() string {
+ return "rule.finalizers.sdewan.akraino.org"
+}
+
+func (m *Mwan3RuleHandler) GetInstance(r client.Client, ctx context.Context, req ctrl.Request) (runtime.Object, error) {
+ instance := &batchv1alpha1.Mwan3Rule{}
+ err := r.Get(ctx, req.NamespacedName, instance)
+ return instance, err
+}
+
+func (m *Mwan3RuleHandler) Convert(instance runtime.Object, deployment appsv1.Deployment) (openwrt.IOpenWrtObject, error) {
+ rule := instance.(*batchv1alpha1.Mwan3Rule)
+ openwrtrule := openwrt.SdewanRule{
+ Name: rule.Name,
+ Policy: rule.Spec.Policy,
+ SrcIp: rule.Spec.SrcIp,
+ SrcPort: rule.Spec.SrcPort,
+ DestIp: rule.Spec.DestIp,
+ DestPort: rule.Spec.DestPort,
+ Proto: rule.Spec.Proto,
+ Family: rule.Spec.Family,
+ Sticky: rule.Spec.Sticky,
+ Timeout: rule.Spec.Timeout,
+ }
+ return &openwrtrule, nil
+}
+
+func (m *Mwan3RuleHandler) IsEqual(instance1 openwrt.IOpenWrtObject, instance2 openwrt.IOpenWrtObject) bool {
+ Rule1 := instance1.(*openwrt.SdewanRule)
+ Rule2 := instance2.(*openwrt.SdewanRule)
+ return reflect.DeepEqual(*Rule1, *Rule2)
+}
+
+func (m *Mwan3RuleHandler) GetObject(clientInfo *openwrt.OpenwrtClientInfo, name string) (openwrt.IOpenWrtObject, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ ret, err := mwan3.GetRule(name)
+ return ret, err
+}
+
+func (m *Mwan3RuleHandler) CreateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ Rule := instance.(*openwrt.SdewanRule)
+ return mwan3.CreateRule(*Rule)
+}
+
+func (m *Mwan3RuleHandler) UpdateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ Rule := instance.(*openwrt.SdewanRule)
+ return mwan3.UpdateRule(*Rule)
+}
+
+func (m *Mwan3RuleHandler) DeleteObject(clientInfo *openwrt.OpenwrtClientInfo, name string) error {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ mwan3 := openwrt.Mwan3Client{OpenwrtClient: openwrtClient}
+ return mwan3.DeleteRule(name)
+}
+
+func (m *Mwan3RuleHandler) Restart(clientInfo *openwrt.OpenwrtClientInfo) (bool, error) {
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ service := openwrt.ServiceClient{OpenwrtClient: openwrtClient}
+ return service.ExecuteService("mwan3", "restart")
+}
+
+// Mwan3RuleReconciler reconciles a Mwan3Rule object
+type Mwan3RuleReconciler struct {
+ client.Client
+ Log logr.Logger
+ Scheme *runtime.Scheme
+}
+
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=mwan3rules,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=mwan3rules/status,verbs=get;update;patch
+
+func (r *Mwan3RuleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
+ return ProcessReconcile(r, r.Log, req, mwan3RuleHandler)
+}
+
+func (r *Mwan3RuleReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ ps := builder.WithPredicates(predicate.GenerationChangedPredicate{})
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&batchv1alpha1.Mwan3Rule{}, ps).
+ Complete(r)
+}
"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"
+ "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
// +kubebuilder:scaffold:imports
)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
- []Reporter{envtest.NewlineReporter{}})
+ []Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
err = batchv1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
+ err = batchv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
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
+ github.com/onsi/ginkgo v1.11.0
+ github.com/onsi/gomega v1.8.1
+ k8s.io/api v0.18.2
+ k8s.io/apimachinery v0.18.2
+ k8s.io/client-go v0.18.2
+ sigs.k8s.io/controller-runtime v0.6.0
)
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 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
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 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
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/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
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-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/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
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/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
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/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/go-units v0.4.0/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/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/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 h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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 h1:ZktWZesgun21uEDrwW7iEV1zPCGQldM2atlJZ3TdvVM=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0/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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
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-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/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
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 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
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 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
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/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
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/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
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 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
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/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
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 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/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/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
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/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
+github.com/gogo/protobuf v1.3.1/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/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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.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/btree v1.0.0/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/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
+github.com/google/gofuzz v1.1.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/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.1.0/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 v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
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/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/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-middleware v1.0.1-0.20190118093823-f849b5445de4/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/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
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/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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
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/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
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/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 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
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/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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
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/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
+github.com/onsi/ginkgo v1.11.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/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
+github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
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/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.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
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_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
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/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
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/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
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/soheilhy/cmux v0.1.4/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.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
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.1/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/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/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/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
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/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
+go.uber.org/atomic v1.4.0/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=
+go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
+golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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/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-20181114220301-adae6a3d119a/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-20181220203305-927f97764cc3/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-20190620200207-3b0461eec859/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/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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/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-20180905080454-ebe1bf3edb33/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-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-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/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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-20190125232054-d66bd3c5d5a6/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-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-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
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=
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/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
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-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
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=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/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/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc=
+k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
+k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8=
+k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
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/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss=
+k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs=
+k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8=
+k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 h1:CS1tBQz3HOXiseWZu6ZicKX361CZLT97UFnnPx0aqBw=
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
+k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4=
+k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
+k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA=
+k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
+k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
+k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
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/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc=
+k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
+k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
+k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
+k8s.io/client-go v1.5.1 h1:XaX/lo2/u3/pmFau8HN+sB5C/b4dc4Dmm2eXjBH4p1E=
+k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
+k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
+k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
+k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
+k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs=
+k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
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/gengo v0.0.0-20200114144118-36b2048a9120/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/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog v1.0.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/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
+k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
+k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
+k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
+k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
+k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/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/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
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/controller-runtime v0.5.0 h1:CbqIy5fbUX+4E9bpnBFd204YAzRYlM9SWW77BbrcDQo=
+sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8=
+sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
+sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
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/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
+sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
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=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
"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"
+
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
+ "sdewan.akraino.org/sdewan/controllers"
// +kubebuilder:scaffold:imports
)
Scheme: scheme,
MetricsBindAddress: metricsAddr,
LeaderElection: enableLeaderElection,
+ LeaderElectionID: "controller-leader-election-helper",
Port: 9443,
})
if err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Mwan3Policy")
os.Exit(1)
}
+ if err = (&controllers.Mwan3RuleReconciler{
+ Client: mgr.GetClient(),
+ Log: ctrl.Log.WithName("controllers").WithName("Mwan3Rule"),
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "Mwan3Rule")
+ os.Exit(1)
+ }
// +kubebuilder:scaffold:builder
setupLog.Info("starting manager")
func (m *FirewallClient) GetZone(zone string) (*SdewanFirewallZone, error) {
var response string
var err error
- response, err = m.OpenwrtClient.Get(firewallBaseURL + "zone/" + zone)
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "zones/" + zone)
if err != nil {
return nil, err
}
var response string
var err error
zone_obj, _ := json.Marshal(zone)
- response, err = m.OpenwrtClient.Post(firewallBaseURL+"zone", string(zone_obj))
+ response, err = m.OpenwrtClient.Post(firewallBaseURL+"zones", string(zone_obj))
if err != nil {
return nil, err
}
// delete zone
func (m *FirewallClient) DeleteZone(zone_name string) error {
- _, err := m.OpenwrtClient.Delete(firewallBaseURL + "zone/" + zone_name)
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "zones/" + zone_name)
if err != nil {
return err
}
var err error
zone_obj, _ := json.Marshal(zone)
zone_name := zone.Name
- response, err = m.OpenwrtClient.Put(firewallBaseURL+"zone/"+zone_name, string(zone_obj))
+ response, err = m.OpenwrtClient.Put(firewallBaseURL+"zones/"+zone_name, string(zone_obj))
if err != nil {
return nil, err
}
func (m *FirewallClient) GetRule(rule string) (*SdewanFirewallRule, error) {
var response string
var err error
- response, err = m.OpenwrtClient.Get(firewallBaseURL + "rule/" + rule)
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "rules/" + rule)
if err != nil {
return nil, err
}
var response string
var err error
rule_obj, _ := json.Marshal(rule)
- response, err = m.OpenwrtClient.Post(firewallBaseURL+"rule", string(rule_obj))
+ response, err = m.OpenwrtClient.Post(firewallBaseURL+"rules", string(rule_obj))
if err != nil {
return nil, err
}
// delete rule
func (m *FirewallClient) DeleteRule(rule_name string) error {
- _, err := m.OpenwrtClient.Delete(firewallBaseURL + "rule/" + rule_name)
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "rules/" + rule_name)
if err != nil {
return err
}
var err error
rule_obj, _ := json.Marshal(rule)
rule_name := rule.Name
- response, err = m.OpenwrtClient.Put(firewallBaseURL+"rule/"+rule_name, string(rule_obj))
+ response, err = m.OpenwrtClient.Put(firewallBaseURL+"rules/"+rule_name, string(rule_obj))
if err != nil {
return nil, err
}
func (m *FirewallClient) GetForwarding(forwarding string) (*SdewanFirewallForwarding, error) {
var response string
var err error
- response, err = m.OpenwrtClient.Get(firewallBaseURL + "forwarding/" + forwarding)
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "forwardings/" + forwarding)
if err != nil {
return nil, err
}
var response string
var err error
forwarding_obj, _ := json.Marshal(forwarding)
- response, err = m.OpenwrtClient.Post(firewallBaseURL+"forwarding", string(forwarding_obj))
+ response, err = m.OpenwrtClient.Post(firewallBaseURL+"forwardings", string(forwarding_obj))
if err != nil {
return nil, err
}
// delete forwarding
func (m *FirewallClient) DeleteForwarding(forwarding_name string) error {
- _, err := m.OpenwrtClient.Delete(firewallBaseURL + "forwarding/" + forwarding_name)
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "forwardings/" + forwarding_name)
if err != nil {
return err
}
var err error
forwarding_obj, _ := json.Marshal(forwarding)
forwarding_name := forwarding.Name
- response, err = m.OpenwrtClient.Put(firewallBaseURL+"forwarding/"+forwarding_name, string(forwarding_obj))
+ response, err = m.OpenwrtClient.Put(firewallBaseURL+"forwardings/"+forwarding_name, string(forwarding_obj))
if err != nil {
return nil, err
}
func (m *FirewallClient) GetRedirect(redirect string) (*SdewanFirewallRedirect, error) {
var response string
var err error
- response, err = m.OpenwrtClient.Get(firewallBaseURL + "redirect/" + redirect)
+ response, err = m.OpenwrtClient.Get(firewallBaseURL + "redirects/" + redirect)
if err != nil {
return nil, err
}
var response string
var err error
redirect_obj, _ := json.Marshal(redirect)
- response, err = m.OpenwrtClient.Post(firewallBaseURL+"redirect", string(redirect_obj))
+ response, err = m.OpenwrtClient.Post(firewallBaseURL+"redirects", string(redirect_obj))
if err != nil {
return nil, err
}
// delete redirect
func (m *FirewallClient) DeleteRedirect(redirect_name string) error {
- _, err := m.OpenwrtClient.Delete(firewallBaseURL + "redirect/" + redirect_name)
+ _, err := m.OpenwrtClient.Delete(firewallBaseURL + "redirects/" + redirect_name)
if err != nil {
return err
}
var err error
redirect_obj, _ := json.Marshal(redirect)
redirect_name := redirect.Name
- response, err = m.OpenwrtClient.Put(firewallBaseURL+"redirect/"+redirect_name, string(redirect_obj))
+ response, err = m.OpenwrtClient.Put(firewallBaseURL+"redirects/"+redirect_name, string(redirect_obj))
if err != nil {
return nil, err
}
func (m *IpsecClient) GetProposal(proposal string) (*SdewanIpsecProposal, error) {
var response string
var err error
- response, err = m.OpenwrtClient.Get(ipsecBaseURL + "proposal/" + proposal)
+ response, err = m.OpenwrtClient.Get(ipsecBaseURL + "proposals/" + proposal)
if err != nil {
return nil, err
}
var response string
var err error
proposal_obj, _ := json.Marshal(proposal)
- response, err = m.OpenwrtClient.Post(ipsecBaseURL+"proposal", string(proposal_obj))
+ response, err = m.OpenwrtClient.Post(ipsecBaseURL+"proposals", string(proposal_obj))
if err != nil {
return nil, err
}
// delete proposal
func (m *IpsecClient) DeleteProposal(proposal_name string) error {
- _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "proposal/" + proposal_name)
+ _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "proposals/" + proposal_name)
if err != nil {
return err
}
var err error
proposal_obj, _ := json.Marshal(proposal)
proposal_name := proposal.Name
- response, err = m.OpenwrtClient.Put(ipsecBaseURL+"proposal/"+proposal_name, string(proposal_obj))
+ response, err = m.OpenwrtClient.Put(ipsecBaseURL+"proposals/"+proposal_name, string(proposal_obj))
if err != nil {
return nil, err
}
func (m *IpsecClient) GetSite(site string) (*SdewanIpsecSite, error) {
var response string
var err error
- response, err = m.OpenwrtClient.Get(ipsecBaseURL + "site/" + site)
+ response, err = m.OpenwrtClient.Get(ipsecBaseURL + "sites/" + site)
if err != nil {
return nil, err
}
var response string
var err error
site_obj, _ := json.Marshal(site)
- response, err = m.OpenwrtClient.Post(ipsecBaseURL+"site", string(site_obj))
+ response, err = m.OpenwrtClient.Post(ipsecBaseURL+"sites", string(site_obj))
if err != nil {
return nil, err
}
// delete site
func (m *IpsecClient) DeleteSite(site_name string) error {
- _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "site/" + site_name)
+ _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "sites/" + site_name)
if err != nil {
return err
}
var err error
site_obj, _ := json.Marshal(site)
site_name := site.Name
- response, err = m.OpenwrtClient.Put(ipsecBaseURL+"site/"+site_name, string(site_obj))
+ response, err = m.OpenwrtClient.Put(ipsecBaseURL+"sites/"+site_name, string(site_obj))
if err != nil {
return nil, err
}
Policies []SdewanPolicy `json:"policies"`
}
+func (o *SdewanPolicy) GetName() string {
+ return o.Name
+}
+
// MWAN3 Rule
type SdewanRule struct {
Name string `json:"name"`
Rules []SdewanRule `json:"rules"`
}
+func (o *SdewanRule) GetName() string {
+ return o.Name
+}
+
// 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 sdewanPolicy SdewanPolicy
err2 := json.Unmarshal([]byte(response), &sdewanPolicy)
if err2 != nil {
"net/http"
"runtime"
"strings"
+ "sync"
)
+type IOpenWrtObject interface {
+ GetName() string
+}
+
type OpenwrtError struct {
Code int
Message string
return fmt.Sprintf("Error Code: %d, Error Message: %s", e.Code, e.Message)
}
+type OpenwrtClientInfo struct {
+ Ip string
+ User string
+ Password string
+}
+
type openwrtClient struct {
- ip string
- user string
- password string
- token string
+ OpenwrtClientInfo
+ token string
+}
+
+type safeOpenwrtClient struct {
+ clients map[string]*openwrtClient
+ mux sync.Mutex
}
+var gclients = safeOpenwrtClient{clients: make(map[string]*openwrtClient)}
+
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: "",
+func GetOpenwrtClient(clientInfo OpenwrtClientInfo) *openwrtClient {
+ return gclients.GetClient(clientInfo.Ip, clientInfo.User, clientInfo.Password)
+}
+
+// SafeOpenwrtClients
+func (s *safeOpenwrtClient) GetClient(ip string, user string, password string) *openwrtClient {
+ s.mux.Lock()
+ defer s.mux.Unlock()
+ key := ip + "-" + user + "-" + password
+ if s.clients[key] == nil {
+ s.clients[key] = &openwrtClient{
+ OpenwrtClientInfo: OpenwrtClientInfo{
+ Ip: ip,
+ User: user,
+ Password: password,
+ },
+ token: "",
+ }
}
- runtime.SetFinalizer(client, CloseClient)
- return client
+ return s.clients[key]
}
// openwrt base URL
func (o *openwrtClient) getBaseURL() string {
- return "http://" + o.ip + "/cgi-bin/luci/"
+ return "http://" + o.Ip + "/cgi-bin/luci/"
}
// login to openwrt http server
}
// login
- login_info := "luci_username=" + o.user + "&luci_password=" + o.password
+ 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")