Skip to content

Commit

Permalink
Support filtering by namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
bpineau committed Jan 19, 2020
1 parent 7465a33 commit 0182cc5
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 10 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Flags:
-v, --log-level string Log level (default "info")
-o, --log-output string Log output (default "stderr")
-r, --log-server string Log server (if using syslog)
-a, --namespace string Only dump objects from this namespace
-n, --no-git Don't version with git
-i, --resync-interval int Full resync interval in seconds (0 to disable) (default 900)
```
Expand All @@ -84,8 +85,7 @@ Flags:

All settings can be passed by command line options, or environment variable, or in
[a yaml configuration file](https://github.com/bpineau/katafygio/blob/master/assets/katafygio.yaml)
(thanks to Viper and Cobra libs). The environment are the same as cli options,
in uppercase, prefixed by "KF", and with underscore instead of dashs. ie.:
The environment are the same as command line options, in uppercase, prefixed by "KF_", and with underscore instead of dashs. ie.:

```
export KF_GIT_URL=https://user:[email protected]/myorg/myrepos.git
Expand All @@ -100,7 +100,7 @@ export KUBECONFIG=/tmp/kconfig
## Installation

You can find pre-built binaries in the [releases](https://github.com/bpineau/katafygio/releases) page,
ready to run on your desktop or in a cluster.
ready to run on your desktop or in a Kubernetes cluster.

We also provide a [docker image](https://hub.docker.com/r/bpineau/katafygio/).

Expand Down
3 changes: 3 additions & 0 deletions assets/katafygio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ resync-interval: 900
# - configmap:kube-system/datadog-leader-elector
# - deployment:default/testdeploy

# Only dump objects belonging to a specific namespace
#namespace:

# Set to true o dump once and exit (instead of continuously dumping new changes)
dump-only: false

Expand Down
5 changes: 3 additions & 2 deletions cmd/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ var (
Use: appName,
Short: "Backup Kubernetes cluster as yaml files",
Long: "Backup Kubernetes cluster as yaml files in a git repository.\n" +
"--exclude-kind (-x) and --exclude-object (-y) may be specified several times.",
"--exclude-kind (-x) and --exclude-object (-y) may be specified several times,\n" +
"or once with several comma separated values.",
SilenceUsage: true,
SilenceErrors: true,
PreRun: bindConf,
Expand Down Expand Up @@ -71,7 +72,7 @@ func runE(cmd *cobra.Command, args []string) (err error) {
evts := event.New()
fact := controller.NewFactory(logger, filter, resyncInt, exclobj)
reco := recorder.New(logger, evts, localDir, resyncInt*2, dryRun).Start()
obsv := observer.New(logger, restcfg, evts, fact, exclkind).Start()
obsv := observer.New(logger, restcfg, evts, fact, exclkind, namespace).Start()

logger.Info(appName, " started")
sigterm := make(chan os.Signal, 1)
Expand Down
7 changes: 6 additions & 1 deletion cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var (
cfgFile string
apiServer string
context string
namespace string
kubeConf string
dryRun bool
dumpMode bool
Expand Down Expand Up @@ -48,6 +49,9 @@ func init() {
RootCmd.PersistentFlags().StringVarP(&context, "context", "q", "", "Kubernetes configuration context")
bindPFlag("context", "context")

RootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "a", "", "Only dump objects from this namespace")
bindPFlag("namespace", "namespace")

RootCmd.PersistentFlags().StringVarP(&kubeConf, "kube-config", "k", "", "Kubernetes configuration path")
bindPFlag("kube-config", "kube-config")

Expand Down Expand Up @@ -81,7 +85,7 @@ func init() {
RootCmd.PersistentFlags().StringSliceVarP(&exclobj, "exclude-object", "y", nil, "Object to exclude. Eg. 'configmap:kube-system/kube-dns'")
bindPFlag("exclude-object", "exclude-object")

RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label.")
RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label")
bindPFlag("filter", "filter")

RootCmd.PersistentFlags().IntVarP(&healthP, "healthcheck-port", "p", 0, "Port for answering healthchecks on /health url")
Expand All @@ -98,6 +102,7 @@ func init() {
func bindConf(cmd *cobra.Command, args []string) {
apiServer = viper.GetString("api-server")
context = viper.GetString("context")
namespace = viper.GetString("namespace")
kubeConf = viper.GetString("kube-config")
dryRun = viper.GetBool("dry-run")
dumpMode = viper.GetBool("dump-only")
Expand Down
12 changes: 11 additions & 1 deletion pkg/observer/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type Observer struct {
factory ControllerFactory
logger logger
excludedkind []string
namespace string
}

type gvk struct {
Expand All @@ -62,7 +63,7 @@ type gvk struct {
type resources map[string]*gvk

// New returns a new observer, that will watch API resources and create controllers
func New(log logger, client restclient, notif event.Notifier, factory ControllerFactory, excluded []string) *Observer {
func New(log logger, client restclient, notif event.Notifier, factory ControllerFactory, excluded []string, namespace string) *Observer {
return &Observer{
notifier: notif,
discovery: discovery.NewDiscoveryClientForConfigOrDie(client.GetRestConfig()),
Expand All @@ -71,6 +72,7 @@ func New(log logger, client restclient, notif event.Notifier, factory Controller
factory: factory,
logger: log,
excludedkind: excluded,
namespace: namespace,
}
}

Expand Down Expand Up @@ -140,6 +142,9 @@ func (c *Observer) refresh() error {

cname := strings.ToLower(res.apiResource.Kind)
namespace := metav1.NamespaceAll
if c.namespace != "" {
namespace = c.namespace
}
lw := &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return c.cpool.Resource(resource).Namespace(namespace).List(options)
Expand Down Expand Up @@ -189,6 +194,11 @@ func (c *Observer) expandAndFilterAPIResources(groups []*metav1.APIResourceList)
continue
}

// ignore non namespaced resources, when we have a namespace filter
if c.namespace != "" && !ar.Namespaced {
continue
}

// only consider resources that are getable, listable an watchable
if !isSubList(ar.Verbs, []string{"list", "get", "watch"}) {
continue
Expand Down
25 changes: 22 additions & 3 deletions pkg/observer/observer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type resTest struct {
resources []*metav1.APIResourceList
exclude []string
expect []string
namespace string
}

var resourcesTests = []resTest{
Expand Down Expand Up @@ -167,12 +168,30 @@ var resourcesTests = []resTest{
},
},
},

{
title: "Eliminate non namespaced",
exclude: []string{},
expect: []string{"bar1", "bar2"},
namespace: "foo",
resources: []*metav1.APIResourceList{
{
GroupVersion: "foo/v42",
APIResources: []metav1.APIResource{
{Name: "bar1", Namespaced: true, Kind: "Bar1", Verbs: stdVerbs},
{Name: "bar2", Namespaced: true, Kind: "Bar2", Verbs: stdVerbs},
{Name: "bar3", Namespaced: false, Kind: "Bar3", Verbs: stdVerbs},
{Name: "bar4", Namespaced: false, Kind: "Bar4", Verbs: stdVerbs},
},
},
},
},
}

func TestObserver(t *testing.T) {
for _, tt := range resourcesTests {
factory := new(mockFactory)
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, tt.exclude)
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, tt.exclude, tt.namespace)

client := fakeclientset.NewSimpleClientset()
fakeDiscovery, _ := client.Discovery().(*fakediscovery.FakeDiscovery)
Expand Down Expand Up @@ -213,7 +232,7 @@ func TestObserverDuplicas(t *testing.T) {
fakeDiscovery.Resources = duplicatesTest

factory := new(mockFactory)
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0))
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0), "")
obs.discovery = fakeDiscovery
obs.Start()
err := obs.refresh()
Expand Down Expand Up @@ -243,7 +262,7 @@ func TestObserverRecoverFromDicoveryFailure(t *testing.T) {
}

factory := new(mockFactory)
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0))
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0), "")

// failing discovery
obs.discovery.RESTClient().(*rest.RESTClient).Client = fakeClient.Client
Expand Down

0 comments on commit 0182cc5

Please sign in to comment.