Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Add --k8s-namespace-whitelist setting that specifies namespaces to watch. #1184

Merged
merged 4 commits into from
Jul 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions cluster/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,12 @@ func isAddon(obj k8sObject) bool {
// Cluster is a handle to a Kubernetes API server.
// (Typically, this code is deployed into the same cluster.)
type Cluster struct {
client extendedClient
applier Applier
version string // string response for the version command.
logger log.Logger
sshKeyRing ssh.KeyRing
client extendedClient
applier Applier
version string // string response for the version command.
logger log.Logger
sshKeyRing ssh.KeyRing
nsWhitelist map[string]bool

mu sync.Mutex
}
Expand All @@ -118,7 +119,13 @@ func NewCluster(clientset k8sclient.Interface,
ifclientset ifclient.Interface,
applier Applier,
sshKeyRing ssh.KeyRing,
logger log.Logger) *Cluster {
logger log.Logger,
nsWhitelist []string) *Cluster {

nsWhitelistMap := map[string]bool{}
for _, namespace := range nsWhitelist {
nsWhitelistMap[namespace] = true
}

c := &Cluster{
client: extendedClient{
Expand All @@ -132,6 +139,7 @@ func NewCluster(clientset k8sclient.Interface,
applier: applier,
logger: logger,
sshKeyRing: sshKeyRing,
nsWhitelist: nsWhitelistMap,
}

return c
Expand Down Expand Up @@ -167,13 +175,13 @@ func (c *Cluster) SomeControllers(ids []flux.ResourceID) (res []cluster.Controll
// AllControllers returns all controllers matching the criteria; that is, in
// the namespace (or any namespace if that argument is empty)
func (c *Cluster) AllControllers(namespace string) (res []cluster.Controller, err error) {
namespaces, err := c.client.Namespaces().List(meta_v1.ListOptions{})
namespaces, err := c.getAllowedNamespaces()
if err != nil {
return nil, errors.Wrap(err, "getting namespaces")
}

var allControllers []cluster.Controller
for _, ns := range namespaces.Items {
for _, ns := range namespaces {
if namespace != "" && ns.Name != namespace {
continue
}
Expand Down Expand Up @@ -252,11 +260,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.getAllowedNamespaces()
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")
Expand Down Expand Up @@ -365,13 +375,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.getAllowedNamespaces()
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 {
Expand Down Expand Up @@ -402,3 +412,27 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds {

return allImageCreds
}

// getAllowedNamespaces 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 whitelist has been set on the Cluster
// instance, in which case it returns a list containing the namespaces from the whitelist
// that exist in the cluster.
func (c *Cluster) getAllowedNamespaces() ([]apiv1.Namespace, error) {
nsList := []apiv1.Namespace{}

namespaces, err := c.client.Namespaces().List(meta_v1.ListOptions{})
if err != nil {
return nsList, err
}

for _, namespace := range namespaces.Items {
if len(c.nsWhitelist) > 0 && ! c.nsWhitelist[namespace.ObjectMeta.Name] {
continue
}

nsList = append(nsList, namespace)
}

return nsList, nil
}
62 changes: 62 additions & 0 deletions cluster/kubernetes/kubernetes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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 testGetAllowedNamespaces(t *testing.T, namespace []string, expected []string) {
clientset := fakekubernetes.NewSimpleClientset(newNamespace("default"),
newNamespace("kube-system"))

c := NewCluster(clientset, nil, nil, nil, nil, namespace)

namespaces, err := c.getAllowedNamespaces()
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 TestGetAllowedNamespacesDefault(t *testing.T) {
testGetAllowedNamespaces(t, []string{}, []string{"default","kube-system",})
}

func TestGetAllowedNamespacesNamespacesIsNil(t *testing.T) {
testGetAllowedNamespaces(t, nil, []string{"default","kube-system",})
}

func TestGetAllowedNamespacesNamespacesSet(t *testing.T) {
testGetAllowedNamespaces(t, []string{"default"}, []string{"default",})
}

func TestGetAllowedNamespacesNamespacesSetDoesNotExist(t *testing.T) {
testGetAllowedNamespaces(t, []string{"hello"}, []string{})
}

func TestGetAllowedNamespacesNamespacesMultiple(t *testing.T) {
testGetAllowedNamespaces(t, []string{"default","hello","kube-system"}, []string{"default","kube-system"})
}
3 changes: 2 additions & 1 deletion cmd/fluxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func main() {
k8sSecretName = fs.String("k8s-secret-name", "flux-git-deploy", "Name of the k8s secret used to store the private SSH key")
k8sSecretVolumeMountPath = fs.String("k8s-secret-volume-mount-path", "/etc/fluxd/ssh", "Mount location of the k8s secret storing the private SSH key")
k8sSecretDataKey = fs.String("k8s-secret-data-key", "identity", "Data key holding the private SSH key within the k8s secret")
k8sNamespaceWhitelist = fs.StringSlice("k8s-namespace-whitelist", []string{}, "Experimental, optional: restrict the view of the cluster to the namespaces listed. All namespaces are included if this is not set.")
// SSH key generation
sshKeyBits = optionalVar(fs, &ssh.KeyBitsValue{}, "ssh-keygen-bits", "-b argument to ssh-keygen (default unspecified)")
sshKeyType = optionalVar(fs, &ssh.KeyTypeValue{}, "ssh-keygen-type", "-t argument to ssh-keygen (default unspecified)")
Expand Down Expand Up @@ -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, logger, *k8sNamespaceWhitelist)

if err := k8sInst.Ping(); err != nil {
logger.Log("ping", err)
Expand Down
3 changes: 3 additions & 0 deletions site/daemon.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ fluxd requires setup and offers customization though a multitude of flags.
|--k8s-secret-name | `flux-git-deploy` | name of the k8s secret used to store the private SSH key|
|--k8s-secret-volume-mount-path | `/etc/fluxd/ssh` | mount location of the k8s secret storing the private SSH key|
|--k8s-secret-data-key | `identity` | data key holding the private SSH key within the k8s secret|
|**k8s configuration** | | | |
|--k8s-namespace-whitelist| | Experimental, optional: restrict the view of the cluster to the namespaces listed. All namespaces are included if this is not set.|
|**upstream service** | | | |
|--connect | | connect to an upstream service e.g., Weave Cloud, at this base address|
|--token | | authentication token for upstream service|
|**SSH key generation** | | |
Expand Down