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

Commit

Permalink
Merge pull request #1184 from justinbarrick/master
Browse files Browse the repository at this point in the history
Add --k8s-namespace-whitelist setting that specifies namespaces to watch.
  • Loading branch information
squaremo authored Jul 4, 2018
2 parents dfa97ba + 0b00b1d commit 34e4f93
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 13 deletions.
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 @@ -242,7 +243,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

0 comments on commit 34e4f93

Please sign in to comment.