diff --git a/cluster/kubernetes/kubernetes.go b/cluster/kubernetes/kubernetes.go index e448f78102..cf43c76e6c 100644 --- a/cluster/kubernetes/kubernetes.go +++ b/cluster/kubernetes/kubernetes.go @@ -109,6 +109,7 @@ type Cluster struct { version string // string response for the version command. logger log.Logger sshKeyRing ssh.KeyRing + namespace string mu sync.Mutex } @@ -118,6 +119,7 @@ func NewCluster(clientset k8sclient.Interface, ifclientset ifclient.Interface, applier Applier, sshKeyRing ssh.KeyRing, + namespace string, logger log.Logger) *Cluster { c := &Cluster{ @@ -132,6 +134,7 @@ func NewCluster(clientset k8sclient.Interface, applier: applier, logger: logger, sshKeyRing: sshKeyRing, + namespace: namespace, } return c @@ -252,11 +255,13 @@ func (c *Cluster) Ping() error { // Export exports cluster resources func (c *Cluster) Export() ([]byte, error) { var config bytes.Buffer - list, err := c.client.Namespaces().List(meta_v1.ListOptions{}) + + namespaces, err := c.GetNamespaces() if err != nil { return nil, errors.Wrap(err, "getting namespaces") } - for _, ns := range list.Items { + + for _, ns := range namespaces { err := appendYAML(&config, "v1", "Namespace", ns) if err != nil { return nil, errors.Wrap(err, "marshalling namespace to YAML") @@ -365,13 +370,13 @@ func mergeCredentials(c *Cluster, namespace string, podTemplate apiv1.PodTemplat func (c *Cluster) ImagesToFetch() registry.ImageCreds { allImageCreds := make(registry.ImageCreds) - namespaces, err := c.client.Namespaces().List(meta_v1.ListOptions{}) + namespaces, err := c.GetNamespaces() if err != nil { c.logger.Log("err", errors.Wrap(err, "getting namespaces")) return allImageCreds } - for _, ns := range namespaces.Items { + for _, ns := range namespaces { for kind, resourceKind := range resourceKinds { podControllers, err := resourceKind.getPodControllers(c, ns.Name) if err != nil { @@ -402,3 +407,33 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { return allImageCreds } + +// GetNamespaces returns a list of namespaces that the Flux instance is expected +// to have access to and can look for resources inside of. +// It returns a list of all namespaces unless a namespace has been set on the Cluster +// instance, in which case it returns a list containing that namespace. +func (c *Cluster) GetNamespaces() ([]apiv1.Namespace, error) { + nsList := []apiv1.Namespace{} + + if c.namespace == "" { + namespaces, err := c.client.Namespaces().List(meta_v1.ListOptions{}) + if err != nil { + return nsList, err + } + + nsList = namespaces.Items + } else { + namespace, err := c.client.Namespaces().Get(c.namespace, meta_v1.GetOptions{}) + if err != nil { + // return empty list and no error if the namespace does not exist. + if apierrors.IsNotFound(err) { + err = nil + } + return nsList, err + } + + nsList = append(nsList, *namespace) + } + + return nsList, nil +} diff --git a/cluster/kubernetes/kubernetes_test.go b/cluster/kubernetes/kubernetes_test.go new file mode 100644 index 0000000000..789b30046f --- /dev/null +++ b/cluster/kubernetes/kubernetes_test.go @@ -0,0 +1,54 @@ +package kubernetes + +import ( + apiv1 "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakekubernetes "k8s.io/client-go/kubernetes/fake" + "testing" + "reflect" +) + +func newNamespace(name string) *apiv1.Namespace { + return &apiv1.Namespace{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: name, + }, + TypeMeta: meta_v1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + } +} + +func testGetNamespaces(t *testing.T, namespace string, expected []string) { + clientset := fakekubernetes.NewSimpleClientset(newNamespace("default"), + newNamespace("kube-system")) + + c := NewCluster(clientset, nil, nil, nil, namespace, nil) + + namespaces, err := c.GetNamespaces() + if err != nil { + t.Errorf("The error should be nil, not: %s", err) + } + + result := []string{} + for _, namespace := range namespaces { + result = append(result, namespace.ObjectMeta.Name) + } + + if reflect.DeepEqual(result, expected) != true { + t.Errorf("Unexpected namespaces: %v != %v.", result, expected) + } +} + +func TestGetNamespacesDefault(t *testing.T) { + testGetNamespaces(t, "", []string{"default","kube-system",}) +} + +func TestGetNamespacesNamespaceSet(t *testing.T) { + testGetNamespaces(t, "default", []string{"default",}) +} + +func TestGetNamespacesNamespaceSetDoesNotExist(t *testing.T) { + testGetNamespaces(t, "hello", []string{}) +} diff --git a/cmd/fluxd/main.go b/cmd/fluxd/main.go index ee747772ae..b6d2e8d0af 100644 --- a/cmd/fluxd/main.go +++ b/cmd/fluxd/main.go @@ -114,6 +114,7 @@ func main() { token = fs.String("token", "", "Authentication token for upstream service") dockerConfig = fs.String("docker-config", "", "path to a docker config to use for image registry credentials") + fluxNamespace = fs.String("flux-namespace", "", "Namespace that Flux will check for resources, if not set all namespaces will be checked.") ) if err := fs.Parse(os.Args[1:]); err != nil { @@ -241,7 +242,7 @@ func main() { logger.Log("kubectl", kubectl) kubectlApplier := kubernetes.NewKubectl(kubectl, restClientConfig) - k8sInst := kubernetes.NewCluster(clientset, ifclientset, kubectlApplier, sshKeyRing, logger) + k8sInst := kubernetes.NewCluster(clientset, ifclientset, kubectlApplier, sshKeyRing, *fluxNamespace, logger) if err := k8sInst.Ping(); err != nil { logger.Log("ping", err)