VPN: orginize nebula code
diff --git a/core/nebula/controller/.gitignore b/core/nebula/controller/.gitignore
new file mode 100644
index 0000000..d1f57bb
--- /dev/null
+++ b/core/nebula/controller/.gitignore
@@ -0,0 +1,2 @@
+controller
+vendor
diff --git a/core/nebula/controller/Makefile b/core/nebula/controller/Makefile
new file mode 100644
index 0000000..3f7a57e
--- /dev/null
+++ b/core/nebula/controller/Makefile
@@ -0,0 +1,28 @@
+clean:
+ rm -f controller web
+
+generate:
+ rm -rf generated
+ ./hack/generate.sh
+
+controller: clean
+ go1.16 mod tidy
+ go1.16 mod vendor
+ go1.16 build -o controller main.go
+
+web: clean
+ go1.16 build -o web web.go
+
+
+# image: clean build
+# docker build --tag=giolekva/rpuppy-arm .
+
+# push: image
+# docker push giolekva/rpuppy-arm:latest
+
+
+# push_arm64: export GOOS=linux
+# push_arm64: export GOARCH=arm64
+# push_arm64: export CGO_ENABLED=0
+# push_arm64: export GO111MODULE=on
+# push_arm64: push
diff --git a/core/nebula/controller/apis/nebula/v1/doc.go b/core/nebula/controller/apis/nebula/v1/doc.go
new file mode 100644
index 0000000..5eb51a0
--- /dev/null
+++ b/core/nebula/controller/apis/nebula/v1/doc.go
@@ -0,0 +1,5 @@
+// +k8s:deepcopy-gen=package
+// +k8s:defaulter-gen=TypeMeta
+// +groupName=lekva.me
+
+package v1
diff --git a/core/nebula/controller/apis/nebula/v1/register.go b/core/nebula/controller/apis/nebula/v1/register.go
new file mode 100644
index 0000000..5586188
--- /dev/null
+++ b/core/nebula/controller/apis/nebula/v1/register.go
@@ -0,0 +1,45 @@
+package v1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+var SchemeGroupVersion = schema.GroupVersion{Group: "lekva.me", Version: "v1"}
+
+var (
+ // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
+ // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
+ SchemeBuilder runtime.SchemeBuilder
+ localSchemeBuilder = &SchemeBuilder
+ AddToScheme = localSchemeBuilder.AddToScheme
+)
+
+func init() {
+ // We only register manually written functions here. The registration of the
+ // generated functions takes place in the generated files. The separation
+ // makes the code compile even when the generated files are missing.
+ localSchemeBuilder.Register(addKnownTypes)
+}
+
+// Resource takes an unqualified resource and returns a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+ return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+// Adds the list of known types to the given scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+ scheme.AddKnownTypes(SchemeGroupVersion,
+ &NebulaCA{},
+ &NebulaCAList{},
+ &NebulaNode{},
+ &NebulaNodeList{},
+ )
+
+ scheme.AddKnownTypes(SchemeGroupVersion,
+ &metav1.Status{},
+ )
+ metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+ return nil
+}
diff --git a/core/nebula/controller/apis/nebula/v1/types.go b/core/nebula/controller/apis/nebula/v1/types.go
new file mode 100644
index 0000000..0a295b7
--- /dev/null
+++ b/core/nebula/controller/apis/nebula/v1/types.go
@@ -0,0 +1,79 @@
+package v1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +genclient
+// genclient:nonNamespaced
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type NebulaCA struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata"`
+
+ Spec NebulaCASpec `json:"spec"`
+ Status NebulaCAStatus `json:"status,omitempty"`
+}
+
+type NebulaCASpec struct {
+ SecretName string `json:"secretName"`
+}
+
+type NebulaCAStatus struct {
+ State NebulaCAState `json:"state,omitempty"`
+ Message string `json:"message,omitempty"`
+}
+
+type NebulaCAState string
+
+const (
+ NebulaCAStateCreating NebulaCAState = "Creating"
+ NebulaCAStateReady NebulaCAState = "Ready"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type NebulaCAList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+
+ Items []NebulaCA `json:"items"`
+}
+
+// +genclient
+// genclient:nonNamespaced
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type NebulaNode struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata"`
+
+ Spec NebulaNodeSpec `json:"spec"`
+ Status NebulaNodeStatus `json:"status,omitempty"`
+}
+
+type NebulaNodeSpec struct {
+ CAName string `json:"caName"`
+ CANamespace string `json:"caNamespace"`
+ IPCidr string `json:"ipCidr"`
+ PubKey string `json:"pubKey"`
+ SecretName string `json:"secretName"`
+}
+
+type NebulaNodeStatus struct {
+ State NebulaNodeState `json:"state,omitempty"`
+ Message string `json:"message,omitempty"`
+}
+
+type NebulaNodeState string
+
+const (
+ NebulaNodeStateCreating NebulaNodeState = "Creating"
+ NebulaNodeStateReady NebulaNodeState = "Ready"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type NebulaNodeList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata"`
+
+ Items []NebulaNode `json:"items"`
+}
diff --git a/core/nebula/controller/apis/nebula/v1/zz_generated.deepcopy.go b/core/nebula/controller/apis/nebula/v1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..e47733d
--- /dev/null
+++ b/core/nebula/controller/apis/nebula/v1/zz_generated.deepcopy.go
@@ -0,0 +1,197 @@
+// +build !ignore_autogenerated
+
+// gen
+
+// Code generated by deepcopy-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NebulaCA) DeepCopyInto(out *NebulaCA) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ out.Status = in.Status
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaCA.
+func (in *NebulaCA) DeepCopy() *NebulaCA {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaCA)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NebulaCA) 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 *NebulaCAList) DeepCopyInto(out *NebulaCAList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]NebulaCA, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaCAList.
+func (in *NebulaCAList) DeepCopy() *NebulaCAList {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaCAList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NebulaCAList) 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 *NebulaCASpec) DeepCopyInto(out *NebulaCASpec) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaCASpec.
+func (in *NebulaCASpec) DeepCopy() *NebulaCASpec {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaCASpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NebulaCAStatus) DeepCopyInto(out *NebulaCAStatus) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaCAStatus.
+func (in *NebulaCAStatus) DeepCopy() *NebulaCAStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaCAStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NebulaNode) DeepCopyInto(out *NebulaNode) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ out.Status = in.Status
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaNode.
+func (in *NebulaNode) DeepCopy() *NebulaNode {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaNode)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NebulaNode) 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 *NebulaNodeList) DeepCopyInto(out *NebulaNodeList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]NebulaNode, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaNodeList.
+func (in *NebulaNodeList) DeepCopy() *NebulaNodeList {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaNodeList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NebulaNodeList) 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 *NebulaNodeSpec) DeepCopyInto(out *NebulaNodeSpec) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaNodeSpec.
+func (in *NebulaNodeSpec) DeepCopy() *NebulaNodeSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaNodeSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NebulaNodeStatus) DeepCopyInto(out *NebulaNodeStatus) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NebulaNodeStatus.
+func (in *NebulaNodeStatus) DeepCopy() *NebulaNodeStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(NebulaNodeStatus)
+ in.DeepCopyInto(out)
+ return out
+}
diff --git a/core/nebula/controller/controllers/ca.go b/core/nebula/controller/controllers/ca.go
new file mode 100644
index 0000000..d31b6cd
--- /dev/null
+++ b/core/nebula/controller/controllers/ca.go
@@ -0,0 +1,410 @@
+package controllers
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+ corev1informers "k8s.io/client-go/informers/core/v1"
+ "k8s.io/client-go/kubernetes"
+ corev1listers "k8s.io/client-go/listers/core/v1"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/util/workqueue"
+ "k8s.io/klog/v2"
+
+ nebulav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ clientset "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ informers "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/nebula/v1"
+ listers "github.com/giolekva/pcloud/core/nebula/generated/listers/nebula/v1"
+)
+
+var secretImmutable = true
+
+type caRef struct {
+ key string
+}
+
+type nodeRef struct {
+ key string
+}
+
+type NebulaController struct {
+ kubeClient kubernetes.Interface
+ nebulaClient clientset.Interface
+ caLister listers.NebulaCALister
+ caSynced cache.InformerSynced
+ nodeLister listers.NebulaNodeLister
+ nodeSynced cache.InformerSynced
+ secretLister corev1listers.SecretLister
+ secretSynced cache.InformerSynced
+ workqueue workqueue.RateLimitingInterface
+
+ nebulaCert string
+}
+
+func NewNebulaController(kubeClient kubernetes.Interface,
+ nebulaClient clientset.Interface,
+ caInformer informers.NebulaCAInformer,
+ nodeInformer informers.NebulaNodeInformer,
+ secretInformer corev1informers.SecretInformer,
+ nebulaCert string) *NebulaController {
+ c := &NebulaController{
+ kubeClient: kubeClient,
+ nebulaClient: nebulaClient,
+ caLister: caInformer.Lister(),
+ caSynced: caInformer.Informer().HasSynced,
+ nodeLister: nodeInformer.Lister(),
+ nodeSynced: nodeInformer.Informer().HasSynced,
+ secretLister: secretInformer.Lister(),
+ secretSynced: secretInformer.Informer().HasSynced,
+ workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Nebula"),
+ nebulaCert: nebulaCert,
+ }
+
+ caInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: c.enqueueCA,
+ UpdateFunc: func(_, o interface{}) {
+ c.enqueueCA(o)
+ },
+ DeleteFunc: func(o interface{}) {
+ },
+ })
+ nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
+ AddFunc: c.enqueueNode,
+ UpdateFunc: func(_, o interface{}) {
+ c.enqueueNode(o)
+ },
+ DeleteFunc: func(o interface{}) {
+ },
+ })
+
+ return c
+}
+
+func (c *NebulaController) enqueueCA(o interface{}) {
+ var key string
+ var err error
+ if key, err = cache.MetaNamespaceKeyFunc(o); err != nil {
+ utilruntime.HandleError(err)
+ return
+ }
+ c.workqueue.Add(caRef{key})
+}
+
+func (c *NebulaController) enqueueNode(o interface{}) {
+ var key string
+ var err error
+ if key, err = cache.MetaNamespaceKeyFunc(o); err != nil {
+ utilruntime.HandleError(err)
+ return
+ }
+ c.workqueue.Add(nodeRef{key})
+}
+
+func (c *NebulaController) Run(workers int, stopCh <-chan struct{}) error {
+ defer utilruntime.HandleCrash()
+ defer c.workqueue.ShutDown()
+ klog.Info("Starting NebulaCA controller")
+ klog.Info("Waiting for informer caches to sync")
+ if ok := cache.WaitForCacheSync(stopCh, c.caSynced, c.nodeSynced, c.secretSynced); !ok {
+ return fmt.Errorf("Failed to wait for caches to sync")
+ }
+ fmt.Println("Starting workers")
+ for i := 0; i < workers; i++ {
+ go wait.Until(c.runWorker, time.Second, stopCh)
+ }
+ fmt.Println("Started workers")
+ <-stopCh
+ fmt.Println("Shutting down workers")
+ return nil
+}
+
+func (c *NebulaController) runWorker() {
+ for c.processNextWorkItem() {
+ }
+}
+
+func (c *NebulaController) processNextWorkItem() bool {
+ o, shutdown := c.workqueue.Get()
+ if shutdown {
+ return false
+ }
+ err := func(o interface{}) error {
+ defer c.workqueue.Done(o)
+ if ref, ok := o.(caRef); ok {
+ if err := c.processCAWithKey(ref.key); err != nil {
+ c.workqueue.AddRateLimited(ref)
+ return fmt.Errorf("Error syncing '%s': %s, requeuing", ref.key, err.Error())
+ }
+ fmt.Printf("Successfully synced CA '%s'\n", ref.key)
+ } else if ref, ok := o.(nodeRef); ok {
+ if err := c.processNodeWithKey(ref.key); err != nil {
+ c.workqueue.AddRateLimited(ref)
+ return fmt.Errorf("Error syncing '%s': %s, requeuing", ref.key, err.Error())
+ }
+ fmt.Printf("Successfully synced Node '%s'\n", ref.key)
+ } else {
+ c.workqueue.Forget(o)
+ utilruntime.HandleError(fmt.Errorf("expected reference in workqueue but got %#v", o))
+ return nil
+ }
+ c.workqueue.Forget(o)
+ return nil
+ }(o)
+ if err != nil {
+ utilruntime.HandleError(err)
+ return true
+ }
+ return true
+}
+
+func (c *NebulaController) processCAWithKey(key string) error {
+ namespace, name, err := cache.SplitMetaNamespaceKey(key)
+ if err != nil {
+ return nil
+ }
+ ca, err := c.getCA(namespace, name)
+ if err != nil {
+ panic(err)
+ }
+ if ca.Status.State == nebulav1.NebulaCAStateReady {
+ fmt.Printf("%s CA is already in Ready state\n", ca.Name)
+ return nil
+ }
+ keyDir, err := generateCAKey(ca.Name, c.nebulaCert)
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(keyDir)
+ secret, err := createSecretFromDir(keyDir)
+ if err != nil {
+ panic(err)
+ }
+ secret.Immutable = &secretImmutable
+ secret.Name = ca.Spec.SecretName
+ _, err = c.kubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
+ if err != nil {
+ panic(err)
+ }
+ err = c.updateCAStatus(ca, nebulav1.NebulaCAStateReady, "Generated credentials")
+ if err != nil {
+ panic(err)
+ }
+ return nil
+}
+
+func (c *NebulaController) processNodeWithKey(key string) error {
+ namespace, name, err := cache.SplitMetaNamespaceKey(key)
+ if err != nil {
+ return nil
+ }
+ node, err := c.getNode(namespace, name)
+ if err != nil {
+ panic(err)
+ }
+ if node.Status.State == nebulav1.NebulaNodeStateReady {
+ fmt.Printf("%s Node is already in Ready state\n", node.Name)
+ return nil
+ }
+ ca, err := c.getCA(node.Spec.CANamespace, node.Spec.CAName)
+ if ca.Status.State != nebulav1.NebulaCAStateReady {
+ return fmt.Errorf("Referenced CA %s is not ready yet.", node.Spec.CAName)
+ }
+ caSecret, err := c.getSecret(ca.Namespace, ca.Spec.SecretName)
+ if err != nil {
+ panic(err)
+ }
+ dir, err := extractSecret(caSecret)
+ if err != nil {
+ panic(err)
+ }
+ if node.Spec.PubKey == "" {
+ if err := generateNodeKey(node.Name, node.Spec.IPCidr, dir, c.nebulaCert); err != nil {
+ panic(err)
+ }
+ } else {
+ if err := generateNodeKeyFromPub(node.Name, node.Spec.IPCidr, node.Spec.PubKey, dir, c.nebulaCert); err != nil {
+ panic(err)
+ }
+ }
+ defer os.RemoveAll(dir)
+ if err := os.Remove(filepath.Join(dir, "ca.key")); err != nil {
+ panic(err)
+ }
+ if err := os.Remove(filepath.Join(dir, "ca.png")); err != nil {
+ panic(err)
+ }
+ secret, err := createSecretFromDir(dir)
+ if err != nil {
+ panic(err)
+ }
+ secret.Immutable = &secretImmutable
+ secret.Name = node.Spec.SecretName
+ _, err = c.kubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
+ if err != nil {
+ panic(err)
+ }
+ err = c.updateNodeStatus(node, nebulav1.NebulaNodeStateReady, "Generated credentials")
+ if err != nil {
+ panic(err)
+ }
+ return nil
+}
+
+func (c *NebulaController) updateCAStatus(ca *nebulav1.NebulaCA, state nebulav1.NebulaCAState, msg string) error {
+ cp := ca.DeepCopy()
+ cp.Status.State = state
+ cp.Status.Message = msg
+ _, err := c.nebulaClient.LekvaV1().NebulaCAs(cp.Namespace).UpdateStatus(context.TODO(), cp, metav1.UpdateOptions{})
+ return err
+}
+
+func (c *NebulaController) updateNodeStatus(node *nebulav1.NebulaNode, state nebulav1.NebulaNodeState, msg string) error {
+ cp := node.DeepCopy()
+ cp.Status.State = state
+ cp.Status.Message = msg
+ _, err := c.nebulaClient.LekvaV1().NebulaNodes(cp.Namespace).UpdateStatus(context.TODO(), cp, metav1.UpdateOptions{})
+ return err
+}
+
+func createSecretFromDir(path string) (*corev1.Secret, error) {
+ all, err := ioutil.ReadDir(path)
+ if err != nil {
+ return nil, err
+ }
+ secret := &corev1.Secret{
+ Data: make(map[string][]byte),
+ }
+ for _, f := range all {
+ if f.IsDir() {
+ continue
+ }
+ d, err := ioutil.ReadFile(filepath.Join(path, f.Name()))
+ if err != nil {
+ return nil, err
+ }
+ secret.Data[f.Name()] = d
+ }
+ return secret, nil
+}
+
+func extractSecret(secret *corev1.Secret) (string, error) {
+ tmp, err := os.MkdirTemp("", secret.Name)
+ if err != nil {
+ return "", err
+ }
+ for name, data := range secret.Data {
+ if err := ioutil.WriteFile(filepath.Join(tmp, name), data, 0644); err != nil {
+ defer os.RemoveAll(tmp)
+ return "", nil
+ }
+ }
+ return tmp, nil
+}
+
+func generateCAKey(name, nebulaCert string) (string, error) {
+ tmp, err := os.MkdirTemp("", name)
+ if err != nil {
+ return "", err
+ }
+ cmd := exec.Command(nebulaCert, "ca",
+ "-name", name,
+ "-out-key", filepath.Join(tmp, "ca.key"),
+ "-out-crt", filepath.Join(tmp, "ca.crt"),
+ "-out-qr", filepath.Join(tmp, "ca.png"))
+ if d, err := cmd.CombinedOutput(); err != nil {
+ return "", fmt.Errorf(string(d))
+ }
+ return tmp, nil
+}
+
+func generateNodeKeyFromPub(name, ip, pubKey, dir, nebulaCert string) error {
+ hostPub := filepath.Join(dir, "host.pub")
+ if err := ioutil.WriteFile(hostPub, []byte(pubKey), 0644); err != nil {
+ return err
+ }
+ defer os.Remove(hostPub)
+ cmd := exec.Command(nebulaCert, "sign",
+ "-ca-crt", filepath.Join(dir, "ca.crt"),
+ "-ca-key", filepath.Join(dir, "ca.key"),
+ "-name", name,
+ "-ip", ip,
+ "-in-pub", hostPub,
+ "-out-crt", filepath.Join(dir, "host.crt"),
+ "-out-qr", filepath.Join(dir, "host.png"))
+ if d, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf(string(d))
+ }
+ return nil
+}
+
+func generateNodeKey(name, ip, dir, nebulaCert string) error {
+ cmd := exec.Command(nebulaCert, "sign",
+ "-ca-crt", filepath.Join(dir, "ca.crt"),
+ "-ca-key", filepath.Join(dir, "ca.key"),
+ "-name", name,
+ "-ip", ip,
+ "-out-key", filepath.Join(dir, "host.key"),
+ "-out-crt", filepath.Join(dir, "host.crt"),
+ "-out-qr", filepath.Join(dir, "host.png"))
+ if d, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf(string(d))
+ }
+ return nil
+}
+
+func (c *NebulaController) getCA(namespace, name string) (*nebulav1.NebulaCA, error) {
+ return c.caLister.NebulaCAs(namespace).Get(name)
+ // s := labels.NewSelector()
+ // r, err := labels.NewRequirement("metadata.namespace", selection.Equals, []string{namespace})
+ // if err != nil {
+ // panic(err)
+ // }
+ // r1, err := labels.NewRequirement("metadata.name", selection.Equals, []string{name})
+ // if err != nil {
+ // panic(err)
+ // }
+ // s.Add(*r, *r1)
+ // ncas, err := c.caLister.List(s)
+ // if err != nil {
+ // panic(err)
+ // }
+ // if len(ncas) != 1 {
+ // panic("err")
+ // }
+ // return ncas[0], nil
+}
+
+func (c *NebulaController) getNode(namespace, name string) (*nebulav1.NebulaNode, error) {
+ return c.nodeLister.NebulaNodes(namespace).Get(name)
+ // s := labels.NewSelector()
+ // r, err := labels.NewRequirement("metadata.namespace", selection.Equals, []string{namespace})
+ // if err != nil {
+ // panic(err)
+ // }
+ // r1, err := labels.NewRequirement("metadata.name", selection.Equals, []string{name})
+ // if err != nil {
+ // panic(err)
+ // }
+ // s.Add(*r, *r1)
+ // nodes, err := c.nodeLister.List(s)
+ // if err != nil {
+ // panic(err)
+ // }
+ // if len(nodes) != 1 {
+ // panic("err")
+ // }
+ // return nodes[0], nil
+}
+
+func (c *NebulaController) getSecret(namespace, name string) (*corev1.Secret, error) {
+ return c.secretLister.Secrets(namespace).Get(name)
+}
diff --git a/core/nebula/controller/crds/nebula.crds.yaml b/core/nebula/controller/crds/nebula.crds.yaml
new file mode 100644
index 0000000..c8de194
--- /dev/null
+++ b/core/nebula/controller/crds/nebula.crds.yaml
@@ -0,0 +1,83 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ name: nebulacas.lekva.me
+spec:
+ group: lekva.me
+ scope: Namespaced
+ names:
+ kind: NebulaCA
+ listKind: NebulaCAList
+ plural: nebulacas
+ singular: nebulaca
+ shortNames:
+ - nca
+ - ncas
+ versions:
+ - name: v1
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ schema:
+ openAPIV3Schema:
+ type: object
+ properties:
+ spec:
+ type: object
+ properties:
+ secretName:
+ type: string
+ status:
+ type: object
+ properties:
+ state:
+ type: string
+ message:
+ type: string
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ name: nebulanodes.lekva.me
+spec:
+ group: lekva.me
+ scope: Namespaced
+ names:
+ kind: NebulaNode
+ listKind: NebulaNodeList
+ plural: nebulanodes
+ singular: nebulanode
+ shortNames:
+ - nnode
+ - nnodes
+ versions:
+ - name: v1
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ schema:
+ openAPIV3Schema:
+ type: object
+ properties:
+ spec:
+ type: object
+ properties:
+ caName:
+ type: string
+ caNamespace:
+ type: string
+ ipCidr:
+ type: string
+ pubKey:
+ type: string
+ secretName:
+ type: string
+ status:
+ type: object
+ properties:
+ state:
+ type: string
+ message:
+ type: string
diff --git a/core/nebula/controller/generated/clientset/versioned/clientset.go b/core/nebula/controller/generated/clientset/versioned/clientset.go
new file mode 100644
index 0000000..32ba8fe
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/clientset.go
@@ -0,0 +1,83 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package versioned
+
+import (
+ "fmt"
+
+ lekvav1 "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/typed/nebula/v1"
+ discovery "k8s.io/client-go/discovery"
+ rest "k8s.io/client-go/rest"
+ flowcontrol "k8s.io/client-go/util/flowcontrol"
+)
+
+type Interface interface {
+ Discovery() discovery.DiscoveryInterface
+ LekvaV1() lekvav1.LekvaV1Interface
+}
+
+// Clientset contains the clients for groups. Each group has exactly one
+// version included in a Clientset.
+type Clientset struct {
+ *discovery.DiscoveryClient
+ lekvaV1 *lekvav1.LekvaV1Client
+}
+
+// LekvaV1 retrieves the LekvaV1Client
+func (c *Clientset) LekvaV1() lekvav1.LekvaV1Interface {
+ return c.lekvaV1
+}
+
+// Discovery retrieves the DiscoveryClient
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+ if c == nil {
+ return nil
+ }
+ return c.DiscoveryClient
+}
+
+// NewForConfig creates a new Clientset for the given config.
+// If config's RateLimiter is not set and QPS and Burst are acceptable,
+// NewForConfig will generate a rate-limiter in configShallowCopy.
+func NewForConfig(c *rest.Config) (*Clientset, error) {
+ configShallowCopy := *c
+ if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
+ if configShallowCopy.Burst <= 0 {
+ return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
+ }
+ configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
+ }
+ var cs Clientset
+ var err error
+ cs.lekvaV1, err = lekvav1.NewForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+
+ cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+ return &cs, nil
+}
+
+// NewForConfigOrDie creates a new Clientset for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *Clientset {
+ var cs Clientset
+ cs.lekvaV1 = lekvav1.NewForConfigOrDie(c)
+
+ cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
+ return &cs
+}
+
+// New creates a new Clientset for the given RESTClient.
+func New(c rest.Interface) *Clientset {
+ var cs Clientset
+ cs.lekvaV1 = lekvav1.New(c)
+
+ cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
+ return &cs
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/doc.go b/core/nebula/controller/generated/clientset/versioned/doc.go
new file mode 100644
index 0000000..51f1905
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/doc.go
@@ -0,0 +1,6 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated clientset.
+package versioned
diff --git a/core/nebula/controller/generated/clientset/versioned/fake/clientset_generated.go b/core/nebula/controller/generated/clientset/versioned/fake/clientset_generated.go
new file mode 100644
index 0000000..b0512f0
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/fake/clientset_generated.go
@@ -0,0 +1,71 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ clientset "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ lekvav1 "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/typed/nebula/v1"
+ fakelekvav1 "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/typed/nebula/v1/fake"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/watch"
+ "k8s.io/client-go/discovery"
+ fakediscovery "k8s.io/client-go/discovery/fake"
+ "k8s.io/client-go/testing"
+)
+
+// NewSimpleClientset returns a clientset that will respond with the provided objects.
+// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
+// without applying any validations and/or defaults. It shouldn't be considered a replacement
+// for a real clientset and is mostly useful in simple unit tests.
+func NewSimpleClientset(objects ...runtime.Object) *Clientset {
+ o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
+ for _, obj := range objects {
+ if err := o.Add(obj); err != nil {
+ panic(err)
+ }
+ }
+
+ cs := &Clientset{tracker: o}
+ cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
+ cs.AddReactor("*", "*", testing.ObjectReaction(o))
+ cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
+ gvr := action.GetResource()
+ ns := action.GetNamespace()
+ watch, err := o.Watch(gvr, ns)
+ if err != nil {
+ return false, nil, err
+ }
+ return true, watch, nil
+ })
+
+ return cs
+}
+
+// Clientset implements clientset.Interface. Meant to be embedded into a
+// struct to get a default implementation. This makes faking out just the method
+// you want to test easier.
+type Clientset struct {
+ testing.Fake
+ discovery *fakediscovery.FakeDiscovery
+ tracker testing.ObjectTracker
+}
+
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+ return c.discovery
+}
+
+func (c *Clientset) Tracker() testing.ObjectTracker {
+ return c.tracker
+}
+
+var (
+ _ clientset.Interface = &Clientset{}
+ _ testing.FakeClient = &Clientset{}
+)
+
+// LekvaV1 retrieves the LekvaV1Client
+func (c *Clientset) LekvaV1() lekvav1.LekvaV1Interface {
+ return &fakelekvav1.FakeLekvaV1{Fake: &c.Fake}
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/fake/doc.go b/core/nebula/controller/generated/clientset/versioned/fake/doc.go
new file mode 100644
index 0000000..82879ea
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/fake/doc.go
@@ -0,0 +1,6 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated fake clientset.
+package fake
diff --git a/core/nebula/controller/generated/clientset/versioned/fake/register.go b/core/nebula/controller/generated/clientset/versioned/fake/register.go
new file mode 100644
index 0000000..aa322cb
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/fake/register.go
@@ -0,0 +1,42 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ lekvav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var scheme = runtime.NewScheme()
+var codecs = serializer.NewCodecFactory(scheme)
+
+var localSchemeBuilder = runtime.SchemeBuilder{
+ lekvav1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+// import (
+// "k8s.io/client-go/kubernetes"
+// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+// )
+//
+// kclientset, _ := kubernetes.NewForConfig(c)
+// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+ v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
+ utilruntime.Must(AddToScheme(scheme))
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/scheme/doc.go b/core/nebula/controller/generated/clientset/versioned/scheme/doc.go
new file mode 100644
index 0000000..59bee07
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/scheme/doc.go
@@ -0,0 +1,6 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package contains the scheme of the automatically generated clientset.
+package scheme
diff --git a/core/nebula/controller/generated/clientset/versioned/scheme/register.go b/core/nebula/controller/generated/clientset/versioned/scheme/register.go
new file mode 100644
index 0000000..e27332c
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/scheme/register.go
@@ -0,0 +1,42 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package scheme
+
+import (
+ lekvav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var Scheme = runtime.NewScheme()
+var Codecs = serializer.NewCodecFactory(Scheme)
+var ParameterCodec = runtime.NewParameterCodec(Scheme)
+var localSchemeBuilder = runtime.SchemeBuilder{
+ lekvav1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+// import (
+// "k8s.io/client-go/kubernetes"
+// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+// )
+//
+// kclientset, _ := kubernetes.NewForConfig(c)
+// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+ v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
+ utilruntime.Must(AddToScheme(Scheme))
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/doc.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/doc.go
new file mode 100644
index 0000000..8ec8188
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/doc.go
@@ -0,0 +1,6 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v1
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/doc.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/doc.go
new file mode 100644
index 0000000..630d59b
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/doc.go
@@ -0,0 +1,6 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// Package fake has the automatically generated clients.
+package fake
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebula_client.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebula_client.go
new file mode 100644
index 0000000..e5f530e
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebula_client.go
@@ -0,0 +1,30 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ v1 "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/typed/nebula/v1"
+ rest "k8s.io/client-go/rest"
+ testing "k8s.io/client-go/testing"
+)
+
+type FakeLekvaV1 struct {
+ *testing.Fake
+}
+
+func (c *FakeLekvaV1) NebulaCAs(namespace string) v1.NebulaCAInterface {
+ return &FakeNebulaCAs{c, namespace}
+}
+
+func (c *FakeLekvaV1) NebulaNodes(namespace string) v1.NebulaNodeInterface {
+ return &FakeNebulaNodes{c, namespace}
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FakeLekvaV1) RESTClient() rest.Interface {
+ var ret *rest.RESTClient
+ return ret
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebulaca.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebulaca.go
new file mode 100644
index 0000000..22182e2
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebulaca.go
@@ -0,0 +1,128 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ nebulav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+)
+
+// FakeNebulaCAs implements NebulaCAInterface
+type FakeNebulaCAs struct {
+ Fake *FakeLekvaV1
+ ns string
+}
+
+var nebulacasResource = schema.GroupVersionResource{Group: "lekva.me", Version: "v1", Resource: "nebulacas"}
+
+var nebulacasKind = schema.GroupVersionKind{Group: "lekva.me", Version: "v1", Kind: "NebulaCA"}
+
+// Get takes name of the nebulaCA, and returns the corresponding nebulaCA object, and an error if there is any.
+func (c *FakeNebulaCAs) Get(ctx context.Context, name string, options v1.GetOptions) (result *nebulav1.NebulaCA, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewGetAction(nebulacasResource, c.ns, name), &nebulav1.NebulaCA{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaCA), err
+}
+
+// List takes label and field selectors, and returns the list of NebulaCAs that match those selectors.
+func (c *FakeNebulaCAs) List(ctx context.Context, opts v1.ListOptions) (result *nebulav1.NebulaCAList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewListAction(nebulacasResource, nebulacasKind, c.ns, opts), &nebulav1.NebulaCAList{})
+
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &nebulav1.NebulaCAList{ListMeta: obj.(*nebulav1.NebulaCAList).ListMeta}
+ for _, item := range obj.(*nebulav1.NebulaCAList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested nebulaCAs.
+func (c *FakeNebulaCAs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewWatchAction(nebulacasResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a nebulaCA and creates it. Returns the server's representation of the nebulaCA, and an error, if there is any.
+func (c *FakeNebulaCAs) Create(ctx context.Context, nebulaCA *nebulav1.NebulaCA, opts v1.CreateOptions) (result *nebulav1.NebulaCA, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewCreateAction(nebulacasResource, c.ns, nebulaCA), &nebulav1.NebulaCA{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaCA), err
+}
+
+// Update takes the representation of a nebulaCA and updates it. Returns the server's representation of the nebulaCA, and an error, if there is any.
+func (c *FakeNebulaCAs) Update(ctx context.Context, nebulaCA *nebulav1.NebulaCA, opts v1.UpdateOptions) (result *nebulav1.NebulaCA, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateAction(nebulacasResource, c.ns, nebulaCA), &nebulav1.NebulaCA{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaCA), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeNebulaCAs) UpdateStatus(ctx context.Context, nebulaCA *nebulav1.NebulaCA, opts v1.UpdateOptions) (*nebulav1.NebulaCA, error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateSubresourceAction(nebulacasResource, "status", c.ns, nebulaCA), &nebulav1.NebulaCA{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaCA), err
+}
+
+// Delete takes name of the nebulaCA and deletes it. Returns an error if one occurs.
+func (c *FakeNebulaCAs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewDeleteAction(nebulacasResource, c.ns, name), &nebulav1.NebulaCA{})
+
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeNebulaCAs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewDeleteCollectionAction(nebulacasResource, c.ns, listOpts)
+
+ _, err := c.Fake.Invokes(action, &nebulav1.NebulaCAList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched nebulaCA.
+func (c *FakeNebulaCAs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *nebulav1.NebulaCA, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewPatchSubresourceAction(nebulacasResource, c.ns, name, pt, data, subresources...), &nebulav1.NebulaCA{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaCA), err
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebulanode.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebulanode.go
new file mode 100644
index 0000000..cbd3957
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/fake/fake_nebulanode.go
@@ -0,0 +1,128 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+ "context"
+
+ nebulav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ labels "k8s.io/apimachinery/pkg/labels"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ testing "k8s.io/client-go/testing"
+)
+
+// FakeNebulaNodes implements NebulaNodeInterface
+type FakeNebulaNodes struct {
+ Fake *FakeLekvaV1
+ ns string
+}
+
+var nebulanodesResource = schema.GroupVersionResource{Group: "lekva.me", Version: "v1", Resource: "nebulanodes"}
+
+var nebulanodesKind = schema.GroupVersionKind{Group: "lekva.me", Version: "v1", Kind: "NebulaNode"}
+
+// Get takes name of the nebulaNode, and returns the corresponding nebulaNode object, and an error if there is any.
+func (c *FakeNebulaNodes) Get(ctx context.Context, name string, options v1.GetOptions) (result *nebulav1.NebulaNode, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewGetAction(nebulanodesResource, c.ns, name), &nebulav1.NebulaNode{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaNode), err
+}
+
+// List takes label and field selectors, and returns the list of NebulaNodes that match those selectors.
+func (c *FakeNebulaNodes) List(ctx context.Context, opts v1.ListOptions) (result *nebulav1.NebulaNodeList, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewListAction(nebulanodesResource, nebulanodesKind, c.ns, opts), &nebulav1.NebulaNodeList{})
+
+ if obj == nil {
+ return nil, err
+ }
+
+ label, _, _ := testing.ExtractFromListOptions(opts)
+ if label == nil {
+ label = labels.Everything()
+ }
+ list := &nebulav1.NebulaNodeList{ListMeta: obj.(*nebulav1.NebulaNodeList).ListMeta}
+ for _, item := range obj.(*nebulav1.NebulaNodeList).Items {
+ if label.Matches(labels.Set(item.Labels)) {
+ list.Items = append(list.Items, item)
+ }
+ }
+ return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested nebulaNodes.
+func (c *FakeNebulaNodes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ return c.Fake.
+ InvokesWatch(testing.NewWatchAction(nebulanodesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a nebulaNode and creates it. Returns the server's representation of the nebulaNode, and an error, if there is any.
+func (c *FakeNebulaNodes) Create(ctx context.Context, nebulaNode *nebulav1.NebulaNode, opts v1.CreateOptions) (result *nebulav1.NebulaNode, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewCreateAction(nebulanodesResource, c.ns, nebulaNode), &nebulav1.NebulaNode{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaNode), err
+}
+
+// Update takes the representation of a nebulaNode and updates it. Returns the server's representation of the nebulaNode, and an error, if there is any.
+func (c *FakeNebulaNodes) Update(ctx context.Context, nebulaNode *nebulav1.NebulaNode, opts v1.UpdateOptions) (result *nebulav1.NebulaNode, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateAction(nebulanodesResource, c.ns, nebulaNode), &nebulav1.NebulaNode{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaNode), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeNebulaNodes) UpdateStatus(ctx context.Context, nebulaNode *nebulav1.NebulaNode, opts v1.UpdateOptions) (*nebulav1.NebulaNode, error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewUpdateSubresourceAction(nebulanodesResource, "status", c.ns, nebulaNode), &nebulav1.NebulaNode{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaNode), err
+}
+
+// Delete takes name of the nebulaNode and deletes it. Returns an error if one occurs.
+func (c *FakeNebulaNodes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ _, err := c.Fake.
+ Invokes(testing.NewDeleteAction(nebulanodesResource, c.ns, name), &nebulav1.NebulaNode{})
+
+ return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeNebulaNodes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ action := testing.NewDeleteCollectionAction(nebulanodesResource, c.ns, listOpts)
+
+ _, err := c.Fake.Invokes(action, &nebulav1.NebulaNodeList{})
+ return err
+}
+
+// Patch applies the patch and returns the patched nebulaNode.
+func (c *FakeNebulaNodes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *nebulav1.NebulaNode, err error) {
+ obj, err := c.Fake.
+ Invokes(testing.NewPatchSubresourceAction(nebulanodesResource, c.ns, name, pt, data, subresources...), &nebulav1.NebulaNode{})
+
+ if obj == nil {
+ return nil, err
+ }
+ return obj.(*nebulav1.NebulaNode), err
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/generated_expansion.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/generated_expansion.go
new file mode 100644
index 0000000..db9c80a
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/generated_expansion.go
@@ -0,0 +1,9 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+type NebulaCAExpansion interface{}
+
+type NebulaNodeExpansion interface{}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebula_client.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebula_client.go
new file mode 100644
index 0000000..261bb36
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebula_client.go
@@ -0,0 +1,80 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ v1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/scheme"
+ rest "k8s.io/client-go/rest"
+)
+
+type LekvaV1Interface interface {
+ RESTClient() rest.Interface
+ NebulaCAsGetter
+ NebulaNodesGetter
+}
+
+// LekvaV1Client is used to interact with features provided by the lekva.me group.
+type LekvaV1Client struct {
+ restClient rest.Interface
+}
+
+func (c *LekvaV1Client) NebulaCAs(namespace string) NebulaCAInterface {
+ return newNebulaCAs(c, namespace)
+}
+
+func (c *LekvaV1Client) NebulaNodes(namespace string) NebulaNodeInterface {
+ return newNebulaNodes(c, namespace)
+}
+
+// NewForConfig creates a new LekvaV1Client for the given config.
+func NewForConfig(c *rest.Config) (*LekvaV1Client, error) {
+ config := *c
+ if err := setConfigDefaults(&config); err != nil {
+ return nil, err
+ }
+ client, err := rest.RESTClientFor(&config)
+ if err != nil {
+ return nil, err
+ }
+ return &LekvaV1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new LekvaV1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *LekvaV1Client {
+ client, err := NewForConfig(c)
+ if err != nil {
+ panic(err)
+ }
+ return client
+}
+
+// New creates a new LekvaV1Client for the given RESTClient.
+func New(c rest.Interface) *LekvaV1Client {
+ return &LekvaV1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+ gv := v1.SchemeGroupVersion
+ config.GroupVersion = &gv
+ config.APIPath = "/apis"
+ config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
+ return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *LekvaV1Client) RESTClient() rest.Interface {
+ if c == nil {
+ return nil
+ }
+ return c.restClient
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebulaca.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebulaca.go
new file mode 100644
index 0000000..11aa718
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebulaca.go
@@ -0,0 +1,181 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ "context"
+ "time"
+
+ v1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ scheme "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/scheme"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// NebulaCAsGetter has a method to return a NebulaCAInterface.
+// A group's client should implement this interface.
+type NebulaCAsGetter interface {
+ NebulaCAs(namespace string) NebulaCAInterface
+}
+
+// NebulaCAInterface has methods to work with NebulaCA resources.
+type NebulaCAInterface interface {
+ Create(ctx context.Context, nebulaCA *v1.NebulaCA, opts metav1.CreateOptions) (*v1.NebulaCA, error)
+ Update(ctx context.Context, nebulaCA *v1.NebulaCA, opts metav1.UpdateOptions) (*v1.NebulaCA, error)
+ UpdateStatus(ctx context.Context, nebulaCA *v1.NebulaCA, opts metav1.UpdateOptions) (*v1.NebulaCA, error)
+ Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
+ Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.NebulaCA, error)
+ List(ctx context.Context, opts metav1.ListOptions) (*v1.NebulaCAList, error)
+ Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.NebulaCA, err error)
+ NebulaCAExpansion
+}
+
+// nebulaCAs implements NebulaCAInterface
+type nebulaCAs struct {
+ client rest.Interface
+ ns string
+}
+
+// newNebulaCAs returns a NebulaCAs
+func newNebulaCAs(c *LekvaV1Client, namespace string) *nebulaCAs {
+ return &nebulaCAs{
+ client: c.RESTClient(),
+ ns: namespace,
+ }
+}
+
+// Get takes name of the nebulaCA, and returns the corresponding nebulaCA object, and an error if there is any.
+func (c *nebulaCAs) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.NebulaCA, err error) {
+ result = &v1.NebulaCA{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of NebulaCAs that match those selectors.
+func (c *nebulaCAs) List(ctx context.Context, opts metav1.ListOptions) (result *v1.NebulaCAList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1.NebulaCAList{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested nebulaCAs.
+func (c *nebulaCAs) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a nebulaCA and creates it. Returns the server's representation of the nebulaCA, and an error, if there is any.
+func (c *nebulaCAs) Create(ctx context.Context, nebulaCA *v1.NebulaCA, opts metav1.CreateOptions) (result *v1.NebulaCA, err error) {
+ result = &v1.NebulaCA{}
+ err = c.client.Post().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(nebulaCA).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a nebulaCA and updates it. Returns the server's representation of the nebulaCA, and an error, if there is any.
+func (c *nebulaCAs) Update(ctx context.Context, nebulaCA *v1.NebulaCA, opts metav1.UpdateOptions) (result *v1.NebulaCA, err error) {
+ result = &v1.NebulaCA{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ Name(nebulaCA.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(nebulaCA).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *nebulaCAs) UpdateStatus(ctx context.Context, nebulaCA *v1.NebulaCA, opts metav1.UpdateOptions) (result *v1.NebulaCA, err error) {
+ result = &v1.NebulaCA{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ Name(nebulaCA.Name).
+ SubResource("status").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(nebulaCA).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the nebulaCA and deletes it. Returns an error if one occurs.
+func (c *nebulaCAs) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *nebulaCAs) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("nebulacas").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched nebulaCA.
+func (c *nebulaCAs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.NebulaCA, err error) {
+ result = &v1.NebulaCA{}
+ err = c.client.Patch(pt).
+ Namespace(c.ns).
+ Resource("nebulacas").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebulanode.go b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebulanode.go
new file mode 100644
index 0000000..4c38986
--- /dev/null
+++ b/core/nebula/controller/generated/clientset/versioned/typed/nebula/v1/nebulanode.go
@@ -0,0 +1,181 @@
+// gen
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ "context"
+ "time"
+
+ v1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ scheme "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/scheme"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// NebulaNodesGetter has a method to return a NebulaNodeInterface.
+// A group's client should implement this interface.
+type NebulaNodesGetter interface {
+ NebulaNodes(namespace string) NebulaNodeInterface
+}
+
+// NebulaNodeInterface has methods to work with NebulaNode resources.
+type NebulaNodeInterface interface {
+ Create(ctx context.Context, nebulaNode *v1.NebulaNode, opts metav1.CreateOptions) (*v1.NebulaNode, error)
+ Update(ctx context.Context, nebulaNode *v1.NebulaNode, opts metav1.UpdateOptions) (*v1.NebulaNode, error)
+ UpdateStatus(ctx context.Context, nebulaNode *v1.NebulaNode, opts metav1.UpdateOptions) (*v1.NebulaNode, error)
+ Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
+ Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.NebulaNode, error)
+ List(ctx context.Context, opts metav1.ListOptions) (*v1.NebulaNodeList, error)
+ Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.NebulaNode, err error)
+ NebulaNodeExpansion
+}
+
+// nebulaNodes implements NebulaNodeInterface
+type nebulaNodes struct {
+ client rest.Interface
+ ns string
+}
+
+// newNebulaNodes returns a NebulaNodes
+func newNebulaNodes(c *LekvaV1Client, namespace string) *nebulaNodes {
+ return &nebulaNodes{
+ client: c.RESTClient(),
+ ns: namespace,
+ }
+}
+
+// Get takes name of the nebulaNode, and returns the corresponding nebulaNode object, and an error if there is any.
+func (c *nebulaNodes) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.NebulaNode, err error) {
+ result = &v1.NebulaNode{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of NebulaNodes that match those selectors.
+func (c *nebulaNodes) List(ctx context.Context, opts metav1.ListOptions) (result *v1.NebulaNodeList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1.NebulaNodeList{}
+ err = c.client.Get().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested nebulaNodes.
+func (c *nebulaNodes) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a nebulaNode and creates it. Returns the server's representation of the nebulaNode, and an error, if there is any.
+func (c *nebulaNodes) Create(ctx context.Context, nebulaNode *v1.NebulaNode, opts metav1.CreateOptions) (result *v1.NebulaNode, err error) {
+ result = &v1.NebulaNode{}
+ err = c.client.Post().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(nebulaNode).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a nebulaNode and updates it. Returns the server's representation of the nebulaNode, and an error, if there is any.
+func (c *nebulaNodes) Update(ctx context.Context, nebulaNode *v1.NebulaNode, opts metav1.UpdateOptions) (result *v1.NebulaNode, err error) {
+ result = &v1.NebulaNode{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ Name(nebulaNode.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(nebulaNode).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *nebulaNodes) UpdateStatus(ctx context.Context, nebulaNode *v1.NebulaNode, opts metav1.UpdateOptions) (result *v1.NebulaNode, err error) {
+ result = &v1.NebulaNode{}
+ err = c.client.Put().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ Name(nebulaNode.Name).
+ SubResource("status").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(nebulaNode).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the nebulaNode and deletes it. Returns an error if one occurs.
+func (c *nebulaNodes) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *nebulaNodes) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched nebulaNode.
+func (c *nebulaNodes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.NebulaNode, err error) {
+ result = &v1.NebulaNode{}
+ err = c.client.Patch(pt).
+ Namespace(c.ns).
+ Resource("nebulanodes").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/core/nebula/controller/generated/informers/externalversions/factory.go b/core/nebula/controller/generated/informers/externalversions/factory.go
new file mode 100644
index 0000000..eef24ab
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/factory.go
@@ -0,0 +1,166 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package externalversions
+
+import (
+ reflect "reflect"
+ sync "sync"
+ time "time"
+
+ versioned "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ internalinterfaces "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/internalinterfaces"
+ nebula "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/nebula"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// SharedInformerOption defines the functional option type for SharedInformerFactory.
+type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
+
+type sharedInformerFactory struct {
+ client versioned.Interface
+ namespace string
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ lock sync.Mutex
+ defaultResync time.Duration
+ customResync map[reflect.Type]time.Duration
+
+ informers map[reflect.Type]cache.SharedIndexInformer
+ // startedInformers is used for tracking which informers have been started.
+ // This allows Start() to be called multiple times safely.
+ startedInformers map[reflect.Type]bool
+}
+
+// WithCustomResyncConfig sets a custom resync period for the specified informer types.
+func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
+ return func(factory *sharedInformerFactory) *sharedInformerFactory {
+ for k, v := range resyncConfig {
+ factory.customResync[reflect.TypeOf(k)] = v
+ }
+ return factory
+ }
+}
+
+// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
+func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
+ return func(factory *sharedInformerFactory) *sharedInformerFactory {
+ factory.tweakListOptions = tweakListOptions
+ return factory
+ }
+}
+
+// WithNamespace limits the SharedInformerFactory to the specified namespace.
+func WithNamespace(namespace string) SharedInformerOption {
+ return func(factory *sharedInformerFactory) *sharedInformerFactory {
+ factory.namespace = namespace
+ return factory
+ }
+}
+
+// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
+func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
+ return NewSharedInformerFactoryWithOptions(client, defaultResync)
+}
+
+// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
+// Listers obtained via this SharedInformerFactory will be subject to the same filters
+// as specified here.
+// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
+func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
+ return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
+}
+
+// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
+func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
+ factory := &sharedInformerFactory{
+ client: client,
+ namespace: v1.NamespaceAll,
+ defaultResync: defaultResync,
+ informers: make(map[reflect.Type]cache.SharedIndexInformer),
+ startedInformers: make(map[reflect.Type]bool),
+ customResync: make(map[reflect.Type]time.Duration),
+ }
+
+ // Apply all options
+ for _, opt := range options {
+ factory = opt(factory)
+ }
+
+ return factory
+}
+
+// Start initializes all requested informers.
+func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ for informerType, informer := range f.informers {
+ if !f.startedInformers[informerType] {
+ go informer.Run(stopCh)
+ f.startedInformers[informerType] = true
+ }
+ }
+}
+
+// WaitForCacheSync waits for all started informers' cache were synced.
+func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
+ informers := func() map[reflect.Type]cache.SharedIndexInformer {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ informers := map[reflect.Type]cache.SharedIndexInformer{}
+ for informerType, informer := range f.informers {
+ if f.startedInformers[informerType] {
+ informers[informerType] = informer
+ }
+ }
+ return informers
+ }()
+
+ res := map[reflect.Type]bool{}
+ for informType, informer := range informers {
+ res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
+ }
+ return res
+}
+
+// InternalInformerFor returns the SharedIndexInformer for obj using an internal
+// client.
+func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ informerType := reflect.TypeOf(obj)
+ informer, exists := f.informers[informerType]
+ if exists {
+ return informer
+ }
+
+ resyncPeriod, exists := f.customResync[informerType]
+ if !exists {
+ resyncPeriod = f.defaultResync
+ }
+
+ informer = newFunc(f.client, resyncPeriod)
+ f.informers[informerType] = informer
+
+ return informer
+}
+
+// SharedInformerFactory provides shared informers for resources in all known
+// API group versions.
+type SharedInformerFactory interface {
+ internalinterfaces.SharedInformerFactory
+ ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
+ WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
+
+ Lekva() nebula.Interface
+}
+
+func (f *sharedInformerFactory) Lekva() nebula.Interface {
+ return nebula.New(f, f.namespace, f.tweakListOptions)
+}
diff --git a/core/nebula/controller/generated/informers/externalversions/generic.go b/core/nebula/controller/generated/informers/externalversions/generic.go
new file mode 100644
index 0000000..9df56e4
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/generic.go
@@ -0,0 +1,50 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package externalversions
+
+import (
+ "fmt"
+
+ v1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
+// sharedInformers based on type
+type GenericInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() cache.GenericLister
+}
+
+type genericInformer struct {
+ informer cache.SharedIndexInformer
+ resource schema.GroupResource
+}
+
+// Informer returns the SharedIndexInformer.
+func (f *genericInformer) Informer() cache.SharedIndexInformer {
+ return f.informer
+}
+
+// Lister returns the GenericLister.
+func (f *genericInformer) Lister() cache.GenericLister {
+ return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
+}
+
+// ForResource gives generic access to a shared informer of the matching type
+// TODO extend this to unknown resources with a client pool
+func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
+ switch resource {
+ // Group=lekva.me, Version=v1
+ case v1.SchemeGroupVersion.WithResource("nebulacas"):
+ return &genericInformer{resource: resource.GroupResource(), informer: f.Lekva().V1().NebulaCAs().Informer()}, nil
+ case v1.SchemeGroupVersion.WithResource("nebulanodes"):
+ return &genericInformer{resource: resource.GroupResource(), informer: f.Lekva().V1().NebulaNodes().Informer()}, nil
+
+ }
+
+ return nil, fmt.Errorf("no informer found for %v", resource)
+}
diff --git a/core/nebula/controller/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/core/nebula/controller/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
new file mode 100644
index 0000000..602d1ad
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
@@ -0,0 +1,26 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package internalinterfaces
+
+import (
+ time "time"
+
+ versioned "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
+type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
+
+// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
+type SharedInformerFactory interface {
+ Start(stopCh <-chan struct{})
+ InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
+}
+
+// TweakListOptionsFunc is a function that transforms a v1.ListOptions.
+type TweakListOptionsFunc func(*v1.ListOptions)
diff --git a/core/nebula/controller/generated/informers/externalversions/nebula/interface.go b/core/nebula/controller/generated/informers/externalversions/nebula/interface.go
new file mode 100644
index 0000000..6061070
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/nebula/interface.go
@@ -0,0 +1,32 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package nebula
+
+import (
+ internalinterfaces "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/internalinterfaces"
+ v1 "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/nebula/v1"
+)
+
+// Interface provides access to each of this group's versions.
+type Interface interface {
+ // V1 provides access to shared informers for resources in V1.
+ V1() v1.Interface
+}
+
+type group struct {
+ factory internalinterfaces.SharedInformerFactory
+ namespace string
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+ return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// V1 returns a new v1.Interface.
+func (g *group) V1() v1.Interface {
+ return v1.New(g.factory, g.namespace, g.tweakListOptions)
+}
diff --git a/core/nebula/controller/generated/informers/externalversions/nebula/v1/interface.go b/core/nebula/controller/generated/informers/externalversions/nebula/v1/interface.go
new file mode 100644
index 0000000..eb7fc27
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/nebula/v1/interface.go
@@ -0,0 +1,38 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ internalinterfaces "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to all the informers in this group version.
+type Interface interface {
+ // NebulaCAs returns a NebulaCAInformer.
+ NebulaCAs() NebulaCAInformer
+ // NebulaNodes returns a NebulaNodeInformer.
+ NebulaNodes() NebulaNodeInformer
+}
+
+type version struct {
+ factory internalinterfaces.SharedInformerFactory
+ namespace string
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+ return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// NebulaCAs returns a NebulaCAInformer.
+func (v *version) NebulaCAs() NebulaCAInformer {
+ return &nebulaCAInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
+
+// NebulaNodes returns a NebulaNodeInformer.
+func (v *version) NebulaNodes() NebulaNodeInformer {
+ return &nebulaNodeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
diff --git a/core/nebula/controller/generated/informers/externalversions/nebula/v1/nebulaca.go b/core/nebula/controller/generated/informers/externalversions/nebula/v1/nebulaca.go
new file mode 100644
index 0000000..eda6a7a
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/nebula/v1/nebulaca.go
@@ -0,0 +1,76 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ "context"
+ time "time"
+
+ nebulav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ versioned "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ internalinterfaces "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/internalinterfaces"
+ v1 "github.com/giolekva/pcloud/core/nebula/generated/listers/nebula/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ watch "k8s.io/apimachinery/pkg/watch"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// NebulaCAInformer provides access to a shared informer and lister for
+// NebulaCAs.
+type NebulaCAInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() v1.NebulaCALister
+}
+
+type nebulaCAInformer struct {
+ factory internalinterfaces.SharedInformerFactory
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ namespace string
+}
+
+// NewNebulaCAInformer constructs a new informer for NebulaCA type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewNebulaCAInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+ return NewFilteredNebulaCAInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredNebulaCAInformer constructs a new informer for NebulaCA type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredNebulaCAInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+ return cache.NewSharedIndexInformer(
+ &cache.ListWatch{
+ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.LekvaV1().NebulaCAs(namespace).List(context.TODO(), options)
+ },
+ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.LekvaV1().NebulaCAs(namespace).Watch(context.TODO(), options)
+ },
+ },
+ &nebulav1.NebulaCA{},
+ resyncPeriod,
+ indexers,
+ )
+}
+
+func (f *nebulaCAInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+ return NewFilteredNebulaCAInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *nebulaCAInformer) Informer() cache.SharedIndexInformer {
+ return f.factory.InformerFor(&nebulav1.NebulaCA{}, f.defaultInformer)
+}
+
+func (f *nebulaCAInformer) Lister() v1.NebulaCALister {
+ return v1.NewNebulaCALister(f.Informer().GetIndexer())
+}
diff --git a/core/nebula/controller/generated/informers/externalversions/nebula/v1/nebulanode.go b/core/nebula/controller/generated/informers/externalversions/nebula/v1/nebulanode.go
new file mode 100644
index 0000000..b3d3c65
--- /dev/null
+++ b/core/nebula/controller/generated/informers/externalversions/nebula/v1/nebulanode.go
@@ -0,0 +1,76 @@
+// gen
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ "context"
+ time "time"
+
+ nebulav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ versioned "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ internalinterfaces "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions/internalinterfaces"
+ v1 "github.com/giolekva/pcloud/core/nebula/generated/listers/nebula/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ watch "k8s.io/apimachinery/pkg/watch"
+ cache "k8s.io/client-go/tools/cache"
+)
+
+// NebulaNodeInformer provides access to a shared informer and lister for
+// NebulaNodes.
+type NebulaNodeInformer interface {
+ Informer() cache.SharedIndexInformer
+ Lister() v1.NebulaNodeLister
+}
+
+type nebulaNodeInformer struct {
+ factory internalinterfaces.SharedInformerFactory
+ tweakListOptions internalinterfaces.TweakListOptionsFunc
+ namespace string
+}
+
+// NewNebulaNodeInformer constructs a new informer for NebulaNode type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewNebulaNodeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+ return NewFilteredNebulaNodeInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredNebulaNodeInformer constructs a new informer for NebulaNode type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredNebulaNodeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+ return cache.NewSharedIndexInformer(
+ &cache.ListWatch{
+ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.LekvaV1().NebulaNodes(namespace).List(context.TODO(), options)
+ },
+ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
+ if tweakListOptions != nil {
+ tweakListOptions(&options)
+ }
+ return client.LekvaV1().NebulaNodes(namespace).Watch(context.TODO(), options)
+ },
+ },
+ &nebulav1.NebulaNode{},
+ resyncPeriod,
+ indexers,
+ )
+}
+
+func (f *nebulaNodeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+ return NewFilteredNebulaNodeInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *nebulaNodeInformer) Informer() cache.SharedIndexInformer {
+ return f.factory.InformerFor(&nebulav1.NebulaNode{}, f.defaultInformer)
+}
+
+func (f *nebulaNodeInformer) Lister() v1.NebulaNodeLister {
+ return v1.NewNebulaNodeLister(f.Informer().GetIndexer())
+}
diff --git a/core/nebula/controller/generated/listers/nebula/v1/expansion_generated.go b/core/nebula/controller/generated/listers/nebula/v1/expansion_generated.go
new file mode 100644
index 0000000..8b4d0e8
--- /dev/null
+++ b/core/nebula/controller/generated/listers/nebula/v1/expansion_generated.go
@@ -0,0 +1,21 @@
+// gen
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1
+
+// NebulaCAListerExpansion allows custom methods to be added to
+// NebulaCALister.
+type NebulaCAListerExpansion interface{}
+
+// NebulaCANamespaceListerExpansion allows custom methods to be added to
+// NebulaCANamespaceLister.
+type NebulaCANamespaceListerExpansion interface{}
+
+// NebulaNodeListerExpansion allows custom methods to be added to
+// NebulaNodeLister.
+type NebulaNodeListerExpansion interface{}
+
+// NebulaNodeNamespaceListerExpansion allows custom methods to be added to
+// NebulaNodeNamespaceLister.
+type NebulaNodeNamespaceListerExpansion interface{}
diff --git a/core/nebula/controller/generated/listers/nebula/v1/nebulaca.go b/core/nebula/controller/generated/listers/nebula/v1/nebulaca.go
new file mode 100644
index 0000000..a261c9d
--- /dev/null
+++ b/core/nebula/controller/generated/listers/nebula/v1/nebulaca.go
@@ -0,0 +1,85 @@
+// gen
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ v1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/client-go/tools/cache"
+)
+
+// NebulaCALister helps list NebulaCAs.
+// All objects returned here must be treated as read-only.
+type NebulaCALister interface {
+ // List lists all NebulaCAs in the indexer.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1.NebulaCA, err error)
+ // NebulaCAs returns an object that can list and get NebulaCAs.
+ NebulaCAs(namespace string) NebulaCANamespaceLister
+ NebulaCAListerExpansion
+}
+
+// nebulaCALister implements the NebulaCALister interface.
+type nebulaCALister struct {
+ indexer cache.Indexer
+}
+
+// NewNebulaCALister returns a new NebulaCALister.
+func NewNebulaCALister(indexer cache.Indexer) NebulaCALister {
+ return &nebulaCALister{indexer: indexer}
+}
+
+// List lists all NebulaCAs in the indexer.
+func (s *nebulaCALister) List(selector labels.Selector) (ret []*v1.NebulaCA, err error) {
+ err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1.NebulaCA))
+ })
+ return ret, err
+}
+
+// NebulaCAs returns an object that can list and get NebulaCAs.
+func (s *nebulaCALister) NebulaCAs(namespace string) NebulaCANamespaceLister {
+ return nebulaCANamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// NebulaCANamespaceLister helps list and get NebulaCAs.
+// All objects returned here must be treated as read-only.
+type NebulaCANamespaceLister interface {
+ // List lists all NebulaCAs in the indexer for a given namespace.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1.NebulaCA, err error)
+ // Get retrieves the NebulaCA from the indexer for a given namespace and name.
+ // Objects returned here must be treated as read-only.
+ Get(name string) (*v1.NebulaCA, error)
+ NebulaCANamespaceListerExpansion
+}
+
+// nebulaCANamespaceLister implements the NebulaCANamespaceLister
+// interface.
+type nebulaCANamespaceLister struct {
+ indexer cache.Indexer
+ namespace string
+}
+
+// List lists all NebulaCAs in the indexer for a given namespace.
+func (s nebulaCANamespaceLister) List(selector labels.Selector) (ret []*v1.NebulaCA, err error) {
+ err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1.NebulaCA))
+ })
+ return ret, err
+}
+
+// Get retrieves the NebulaCA from the indexer for a given namespace and name.
+func (s nebulaCANamespaceLister) Get(name string) (*v1.NebulaCA, error) {
+ obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+ if err != nil {
+ return nil, err
+ }
+ if !exists {
+ return nil, errors.NewNotFound(v1.Resource("nebulaca"), name)
+ }
+ return obj.(*v1.NebulaCA), nil
+}
diff --git a/core/nebula/controller/generated/listers/nebula/v1/nebulanode.go b/core/nebula/controller/generated/listers/nebula/v1/nebulanode.go
new file mode 100644
index 0000000..f861e18
--- /dev/null
+++ b/core/nebula/controller/generated/listers/nebula/v1/nebulanode.go
@@ -0,0 +1,85 @@
+// gen
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ v1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/client-go/tools/cache"
+)
+
+// NebulaNodeLister helps list NebulaNodes.
+// All objects returned here must be treated as read-only.
+type NebulaNodeLister interface {
+ // List lists all NebulaNodes in the indexer.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1.NebulaNode, err error)
+ // NebulaNodes returns an object that can list and get NebulaNodes.
+ NebulaNodes(namespace string) NebulaNodeNamespaceLister
+ NebulaNodeListerExpansion
+}
+
+// nebulaNodeLister implements the NebulaNodeLister interface.
+type nebulaNodeLister struct {
+ indexer cache.Indexer
+}
+
+// NewNebulaNodeLister returns a new NebulaNodeLister.
+func NewNebulaNodeLister(indexer cache.Indexer) NebulaNodeLister {
+ return &nebulaNodeLister{indexer: indexer}
+}
+
+// List lists all NebulaNodes in the indexer.
+func (s *nebulaNodeLister) List(selector labels.Selector) (ret []*v1.NebulaNode, err error) {
+ err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1.NebulaNode))
+ })
+ return ret, err
+}
+
+// NebulaNodes returns an object that can list and get NebulaNodes.
+func (s *nebulaNodeLister) NebulaNodes(namespace string) NebulaNodeNamespaceLister {
+ return nebulaNodeNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// NebulaNodeNamespaceLister helps list and get NebulaNodes.
+// All objects returned here must be treated as read-only.
+type NebulaNodeNamespaceLister interface {
+ // List lists all NebulaNodes in the indexer for a given namespace.
+ // Objects returned here must be treated as read-only.
+ List(selector labels.Selector) (ret []*v1.NebulaNode, err error)
+ // Get retrieves the NebulaNode from the indexer for a given namespace and name.
+ // Objects returned here must be treated as read-only.
+ Get(name string) (*v1.NebulaNode, error)
+ NebulaNodeNamespaceListerExpansion
+}
+
+// nebulaNodeNamespaceLister implements the NebulaNodeNamespaceLister
+// interface.
+type nebulaNodeNamespaceLister struct {
+ indexer cache.Indexer
+ namespace string
+}
+
+// List lists all NebulaNodes in the indexer for a given namespace.
+func (s nebulaNodeNamespaceLister) List(selector labels.Selector) (ret []*v1.NebulaNode, err error) {
+ err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+ ret = append(ret, m.(*v1.NebulaNode))
+ })
+ return ret, err
+}
+
+// Get retrieves the NebulaNode from the indexer for a given namespace and name.
+func (s nebulaNodeNamespaceLister) Get(name string) (*v1.NebulaNode, error) {
+ obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+ if err != nil {
+ return nil, err
+ }
+ if !exists {
+ return nil, errors.NewNotFound(v1.Resource("nebulanode"), name)
+ }
+ return obj.(*v1.NebulaNode), nil
+}
diff --git a/core/nebula/controller/go.mod b/core/nebula/controller/go.mod
new file mode 100644
index 0000000..ea547d2
--- /dev/null
+++ b/core/nebula/controller/go.mod
@@ -0,0 +1,13 @@
+module github.com/giolekva/pcloud/core/nebula
+
+go 1.16
+
+require (
+ github.com/gorilla/mux v1.8.0
+ k8s.io/api v0.22.2
+ k8s.io/apimachinery v0.22.2
+ k8s.io/client-go v0.22.2
+ k8s.io/code-generator v0.22.2
+ k8s.io/klog/v2 v2.9.0
+ k8s.io/sample-controller v0.22.2
+)
diff --git a/core/nebula/controller/go.sum b/core/nebula/controller/go.sum
new file mode 100644
index 0000000..0bfb743
--- /dev/null
+++ b/core/nebula/controller/go.sum
@@ -0,0 +1,487 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
+github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+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/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+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.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
+github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
+github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
+github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
+github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+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/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/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/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
+github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
+github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/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/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+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/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
+golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
+golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/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-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/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-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+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.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
+k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
+k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk=
+k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
+k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc=
+k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U=
+k8s.io/code-generator v0.22.2 h1:+bUv9lpTnAWABtPkvO4x0kfz7j/kDEchVt0P/wXU3jQ=
+k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027 h1:Uusb3oh8XcdzDF/ndlI4ToKTYVlkCSJP39SRY2mfRAw=
+k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
+k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
+k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
+k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
+k8s.io/sample-controller v0.22.2 h1:kMZlLBgN4BMgw1ke1IrNUHdCQSZeliMbxCI8uaOoPJc=
+k8s.io/sample-controller v0.22.2/go.mod h1:MnV5FrZ9NwPuttiIl4LGOaaXl5sXMH3OBYXURbXTcv0=
+k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
+k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
+sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/core/nebula/controller/hack/boilerplate.go.txt b/core/nebula/controller/hack/boilerplate.go.txt
new file mode 100644
index 0000000..6cd5f6e
--- /dev/null
+++ b/core/nebula/controller/hack/boilerplate.go.txt
@@ -0,0 +1 @@
+// gen
diff --git a/core/nebula/controller/hack/generate.sh b/core/nebula/controller/hack/generate.sh
new file mode 100755
index 0000000..c022cc3
--- /dev/null
+++ b/core/nebula/controller/hack/generate.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+bash vendor/k8s.io/code-generator/generate-groups.sh \
+ all \
+ github.com/giolekva/pcloud/core/nebula/generated \
+ github.com/giolekva/pcloud/core/nebula/apis \
+ "nebula:v1" \
+ --go-header-file hack/boilerplate.go.txt
diff --git a/core/nebula/controller/hack/tools.go b/core/nebula/controller/hack/tools.go
new file mode 100644
index 0000000..57051d8
--- /dev/null
+++ b/core/nebula/controller/hack/tools.go
@@ -0,0 +1,3 @@
+package hack
+
+import _ "k8s.io/code-generator"
diff --git a/core/nebula/controller/main.go b/core/nebula/controller/main.go
new file mode 100644
index 0000000..9abfc9e
--- /dev/null
+++ b/core/nebula/controller/main.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "flag"
+ "time"
+
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ kubeinformers "k8s.io/client-go/informers"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/tools/clientcmd"
+
+ controllers "github.com/giolekva/pcloud/core/nebula/controllers"
+ clientset "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+ "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned/scheme"
+ informers "github.com/giolekva/pcloud/core/nebula/generated/informers/externalversions"
+
+ nebulascheme "k8s.io/sample-controller/pkg/generated/clientset/versioned/scheme"
+)
+
+var kubeConfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
+var masterURL = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
+var nebulaCert = flag.String("nebula-cert", "", "Path to the nebula-cert binary.")
+
+func main() {
+ flag.Parse()
+ cfg, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeConfig)
+ if err != nil {
+ panic(err)
+ }
+ kubeClient, err := kubernetes.NewForConfig(cfg)
+ if err != nil {
+ panic(err)
+ }
+ nebulaClient := clientset.NewForConfigOrDie(cfg)
+ utilruntime.Must(nebulascheme.AddToScheme(scheme.Scheme))
+ kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
+ nebulaInformerFactory := informers.NewSharedInformerFactory(nebulaClient, 5*time.Second)
+ c := controllers.NewNebulaController(
+ kubeClient,
+ nebulaClient,
+ nebulaInformerFactory.Lekva().V1().NebulaCAs(),
+ nebulaInformerFactory.Lekva().V1().NebulaNodes(),
+ kubeInformerFactory.Core().V1().Secrets(),
+ *nebulaCert)
+ stopCh := make(chan struct{})
+ kubeInformerFactory.Start(stopCh)
+ nebulaInformerFactory.Start(stopCh)
+ if err := c.Run(1, stopCh); err != nil {
+ panic(err)
+ }
+}
diff --git a/core/nebula/controller/templates/index.html b/core/nebula/controller/templates/index.html
new file mode 100644
index 0000000..ac5ae32
--- /dev/null
+++ b/core/nebula/controller/templates/index.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <title>Nebula Mesh VPN Manager</title>
+</head>
+<body>
+ <form action="/sign-node" method="POST">
+ <label for="ca-name">CA Name:</label><br />
+ <input type="text" name="ca-name" /><br />
+ <label for="ca-namespace">CA Namespace:</label><br />
+ <input type="text" name="ca-namespace" /><br />
+ <label for="node-name">Node Name:</label><br />
+ <input type="text" name="node-name" /><br />
+ <label for="node-namespace">Node Namespace:</label><br />
+ <input type="text" name="node-namespace" /><br />
+ <label for="ip-cidr">IP/CIDR:</label><br />
+ <input type="text" name="ip-cidr" /><br />
+ <label for="pub-key">Public Key:</label><br />
+ <textarea name="pub-key">Put node public key here</textarea><br />
+ <input type="submit" value="Sign node key" />
+ </form>
+ {{range .}}
+ <a href="/ca/{{.Namespace}}/{{.Name}}"><h1>{{.Name}}</h1></a>
+ <table>
+ <tr>
+ <th>Node</th>
+ <th>IP</th>
+ </tr>
+ {{range .Nodes}}
+ <tr>
+ <td>
+ <a href="/node/{{.Namespace}}/{{.Name}}">{{.Name}}</a>
+ </td>
+ <td>
+ {{.IP}}
+ </td>
+ </tr>
+ {{end}}
+ </table>
+ {{end}}
+</body>
+</html>
+
diff --git a/core/nebula/controller/web.go b/core/nebula/controller/web.go
new file mode 100644
index 0000000..8738cd4
--- /dev/null
+++ b/core/nebula/controller/web.go
@@ -0,0 +1,225 @@
+package main
+
+import (
+ "context"
+ "embed"
+ "flag"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+
+ "github.com/gorilla/mux"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/tools/clientcmd"
+
+ nebulav1 "github.com/giolekva/pcloud/core/nebula/apis/nebula/v1"
+ clientset "github.com/giolekva/pcloud/core/nebula/generated/clientset/versioned"
+)
+
+var port = flag.Int("port", 8080, "Port to listen on.")
+var kubeConfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
+var masterURL = flag.String("master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
+
+//go:embed templates/*
+var tmpls embed.FS
+
+type Templates struct {
+ Index *template.Template
+}
+
+func ParseTemplates(fs embed.FS) (*Templates, error) {
+ index, err := template.ParseFS(fs, "templates/index.html")
+ if err != nil {
+ return nil, err
+ }
+ return &Templates{index}, nil
+}
+
+type nebulaCA struct {
+ Name string
+ Namespace string
+ Nodes []nebulaNode
+}
+
+type nebulaNode struct {
+ Name string
+ Namespace string
+ IP string
+}
+
+type Manager struct {
+ kubeClient kubernetes.Interface
+ nebulaClient clientset.Interface
+}
+
+func (m *Manager) ListAll() ([]*nebulaCA, error) {
+ ret := make([]*nebulaCA, 0)
+ cas, err := m.nebulaClient.LekvaV1().NebulaCAs("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ return nil, err
+ }
+ for _, ca := range cas.Items {
+ ret = append(ret, &nebulaCA{
+ Name: ca.Name,
+ Namespace: ca.Namespace,
+ Nodes: make([]nebulaNode, 0),
+ })
+ }
+ nodes, err := m.nebulaClient.LekvaV1().NebulaNodes("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ return nil, err
+ }
+ for _, node := range nodes.Items {
+ for _, ca := range ret {
+ if ca.Name == node.Spec.CAName {
+ ca.Nodes = append(ca.Nodes, nebulaNode{
+ Name: node.Name,
+ Namespace: node.Namespace,
+ IP: node.Spec.IPCidr,
+ })
+ }
+ }
+ }
+ return ret, nil
+}
+
+func (m *Manager) createNode(namespace, name, caNamespace, caName, ipCidr, pubKey string) (string, string, error) {
+ node := &nebulav1.NebulaNode{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Spec: nebulav1.NebulaNodeSpec{
+ CAName: caName,
+ CANamespace: caNamespace,
+ IPCidr: ipCidr,
+ PubKey: pubKey,
+ SecretName: fmt.Sprintf("%s-cert", name),
+ },
+ }
+ node, err := m.nebulaClient.LekvaV1().NebulaNodes(namespace).Create(context.TODO(), node, metav1.CreateOptions{})
+ if err != nil {
+ return "", "", err
+ }
+ return node.Namespace, node.Name, nil
+}
+
+func (m *Manager) getNodeCertQR(namespace, name string) ([]byte, error) {
+ node, err := m.nebulaClient.LekvaV1().NebulaNodes(namespace).Get(context.TODO(), name, metav1.GetOptions{})
+ if err != nil {
+ return nil, err
+ }
+ secret, err := m.kubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), node.Spec.SecretName, metav1.GetOptions{})
+ if err != nil {
+ return nil, err
+ }
+ return secret.Data["host.png"], nil
+}
+
+func (m *Manager) getCACertQR(namespace, name string) ([]byte, error) {
+ ca, err := m.nebulaClient.LekvaV1().NebulaCAs(namespace).Get(context.TODO(), name, metav1.GetOptions{})
+ if err != nil {
+ return nil, err
+ }
+ secret, err := m.kubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), ca.Spec.SecretName, metav1.GetOptions{})
+ if err != nil {
+ return nil, err
+ }
+ return secret.Data["ca.png"], nil
+}
+
+type Handler struct {
+ mgr Manager
+ tmpls *Templates
+}
+
+func (h *Handler) handleIndex(w http.ResponseWriter, r *http.Request) {
+ cas, err := h.mgr.ListAll()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if err := h.tmpls.Index.Execute(w, cas); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func (h *Handler) handleNode(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ namespace := vars["namespace"]
+ name := vars["name"]
+ qr, err := h.mgr.getNodeCertQR(namespace, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ w.Header().Set("Content-Type", "img/png")
+ w.Write(qr)
+}
+
+func (h *Handler) handleCA(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ namespace := vars["namespace"]
+ name := vars["name"]
+ qr, err := h.mgr.getCACertQR(namespace, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ w.Header().Set("Content-Type", "img/png")
+ w.Write(qr)
+}
+
+func (h *Handler) handleSignNode(w http.ResponseWriter, r *http.Request) {
+ if err := r.ParseForm(); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ _, _, err := h.mgr.createNode(
+ r.FormValue("node-namespace"),
+ r.FormValue("node-name"),
+ r.FormValue("ca-namespace"),
+ r.FormValue("ca-name"),
+ r.FormValue("ip-cidr"),
+ r.FormValue("pub-key"),
+ )
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, "/", http.StatusSeeOther)
+}
+
+func main() {
+ flag.Parse()
+ cfg, err := clientcmd.BuildConfigFromFlags(*masterURL, *kubeConfig)
+ if err != nil {
+ panic(err)
+ }
+ kubeClient, err := kubernetes.NewForConfig(cfg)
+ if err != nil {
+ panic(err)
+ }
+ nebulaClient := clientset.NewForConfigOrDie(cfg)
+ t, err := ParseTemplates(tmpls)
+ if err != nil {
+ log.Fatal(err)
+ }
+ mgr := Manager{
+ kubeClient: kubeClient,
+ nebulaClient: nebulaClient,
+ }
+ handler := Handler{
+ mgr: mgr,
+ tmpls: t,
+ }
+ r := mux.NewRouter()
+ r.HandleFunc("/node/{namespace:[a-zA-z0-9-]+}/{name:[a-zA-z0-9-]+}", handler.handleNode)
+ r.HandleFunc("/ca/{namespace:[a-zA-z0-9-]+}/{name:[a-zA-z0-9-]+}", handler.handleCA)
+ r.HandleFunc("/sign-node", handler.handleSignNode)
+ r.HandleFunc("/", handler.handleIndex)
+ http.Handle("/", r)
+ fmt.Printf("Starting HTTP server on port: %d\n", *port)
+ log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
+}