Skip to content

Commit 0182cc5

Browse files
committed
Support filtering by namespace
1 parent 7465a33 commit 0182cc5

File tree

6 files changed

+48
-10
lines changed

6 files changed

+48
-10
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Flags:
7676
-v, --log-level string Log level (default "info")
7777
-o, --log-output string Log output (default "stderr")
7878
-r, --log-server string Log server (if using syslog)
79+
-a, --namespace string Only dump objects from this namespace
7980
-n, --no-git Don't version with git
8081
-i, --resync-interval int Full resync interval in seconds (0 to disable) (default 900)
8182
```
@@ -84,8 +85,7 @@ Flags:
8485

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

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

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

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

assets/katafygio.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ resync-interval: 900
4545
# - configmap:kube-system/datadog-leader-elector
4646
# - deployment:default/testdeploy
4747

48+
# Only dump objects belonging to a specific namespace
49+
#namespace:
50+
4851
# Set to true o dump once and exit (instead of continuously dumping new changes)
4952
dump-only: false
5053

cmd/execute.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ var (
3131
Use: appName,
3232
Short: "Backup Kubernetes cluster as yaml files",
3333
Long: "Backup Kubernetes cluster as yaml files in a git repository.\n" +
34-
"--exclude-kind (-x) and --exclude-object (-y) may be specified several times.",
34+
"--exclude-kind (-x) and --exclude-object (-y) may be specified several times,\n" +
35+
"or once with several comma separated values.",
3536
SilenceUsage: true,
3637
SilenceErrors: true,
3738
PreRun: bindConf,
@@ -71,7 +72,7 @@ func runE(cmd *cobra.Command, args []string) (err error) {
7172
evts := event.New()
7273
fact := controller.NewFactory(logger, filter, resyncInt, exclobj)
7374
reco := recorder.New(logger, evts, localDir, resyncInt*2, dryRun).Start()
74-
obsv := observer.New(logger, restcfg, evts, fact, exclkind).Start()
75+
obsv := observer.New(logger, restcfg, evts, fact, exclkind, namespace).Start()
7576

7677
logger.Info(appName, " started")
7778
sigterm := make(chan os.Signal, 1)

cmd/flags.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var (
1212
cfgFile string
1313
apiServer string
1414
context string
15+
namespace string
1516
kubeConf string
1617
dryRun bool
1718
dumpMode bool
@@ -48,6 +49,9 @@ func init() {
4849
RootCmd.PersistentFlags().StringVarP(&context, "context", "q", "", "Kubernetes configuration context")
4950
bindPFlag("context", "context")
5051

52+
RootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "a", "", "Only dump objects from this namespace")
53+
bindPFlag("namespace", "namespace")
54+
5155
RootCmd.PersistentFlags().StringVarP(&kubeConf, "kube-config", "k", "", "Kubernetes configuration path")
5256
bindPFlag("kube-config", "kube-config")
5357

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

84-
RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label.")
88+
RootCmd.PersistentFlags().StringVarP(&filter, "filter", "l", "", "Label filter. Select only objects matching the label")
8589
bindPFlag("filter", "filter")
8690

8791
RootCmd.PersistentFlags().IntVarP(&healthP, "healthcheck-port", "p", 0, "Port for answering healthchecks on /health url")
@@ -98,6 +102,7 @@ func init() {
98102
func bindConf(cmd *cobra.Command, args []string) {
99103
apiServer = viper.GetString("api-server")
100104
context = viper.GetString("context")
105+
namespace = viper.GetString("namespace")
101106
kubeConf = viper.GetString("kube-config")
102107
dryRun = viper.GetBool("dry-run")
103108
dumpMode = viper.GetBool("dump-only")

pkg/observer/observer.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type Observer struct {
5252
factory ControllerFactory
5353
logger logger
5454
excludedkind []string
55+
namespace string
5556
}
5657

5758
type gvk struct {
@@ -62,7 +63,7 @@ type gvk struct {
6263
type resources map[string]*gvk
6364

6465
// New returns a new observer, that will watch API resources and create controllers
65-
func New(log logger, client restclient, notif event.Notifier, factory ControllerFactory, excluded []string) *Observer {
66+
func New(log logger, client restclient, notif event.Notifier, factory ControllerFactory, excluded []string, namespace string) *Observer {
6667
return &Observer{
6768
notifier: notif,
6869
discovery: discovery.NewDiscoveryClientForConfigOrDie(client.GetRestConfig()),
@@ -71,6 +72,7 @@ func New(log logger, client restclient, notif event.Notifier, factory Controller
7172
factory: factory,
7273
logger: log,
7374
excludedkind: excluded,
75+
namespace: namespace,
7476
}
7577
}
7678

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

141143
cname := strings.ToLower(res.apiResource.Kind)
142144
namespace := metav1.NamespaceAll
145+
if c.namespace != "" {
146+
namespace = c.namespace
147+
}
143148
lw := &cache.ListWatch{
144149
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
145150
return c.cpool.Resource(resource).Namespace(namespace).List(options)
@@ -189,6 +194,11 @@ func (c *Observer) expandAndFilterAPIResources(groups []*metav1.APIResourceList)
189194
continue
190195
}
191196

197+
// ignore non namespaced resources, when we have a namespace filter
198+
if c.namespace != "" && !ar.Namespaced {
199+
continue
200+
}
201+
192202
// only consider resources that are getable, listable an watchable
193203
if !isSubList(ar.Verbs, []string{"list", "get", "watch"}) {
194204
continue

pkg/observer/observer_test.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type resTest struct {
6262
resources []*metav1.APIResourceList
6363
exclude []string
6464
expect []string
65+
namespace string
6566
}
6667

6768
var resourcesTests = []resTest{
@@ -167,12 +168,30 @@ var resourcesTests = []resTest{
167168
},
168169
},
169170
},
171+
172+
{
173+
title: "Eliminate non namespaced",
174+
exclude: []string{},
175+
expect: []string{"bar1", "bar2"},
176+
namespace: "foo",
177+
resources: []*metav1.APIResourceList{
178+
{
179+
GroupVersion: "foo/v42",
180+
APIResources: []metav1.APIResource{
181+
{Name: "bar1", Namespaced: true, Kind: "Bar1", Verbs: stdVerbs},
182+
{Name: "bar2", Namespaced: true, Kind: "Bar2", Verbs: stdVerbs},
183+
{Name: "bar3", Namespaced: false, Kind: "Bar3", Verbs: stdVerbs},
184+
{Name: "bar4", Namespaced: false, Kind: "Bar4", Verbs: stdVerbs},
185+
},
186+
},
187+
},
188+
},
170189
}
171190

172191
func TestObserver(t *testing.T) {
173192
for _, tt := range resourcesTests {
174193
factory := new(mockFactory)
175-
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, tt.exclude)
194+
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, tt.exclude, tt.namespace)
176195

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

215234
factory := new(mockFactory)
216-
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0))
235+
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0), "")
217236
obs.discovery = fakeDiscovery
218237
obs.Start()
219238
err := obs.refresh()
@@ -243,7 +262,7 @@ func TestObserverRecoverFromDicoveryFailure(t *testing.T) {
243262
}
244263

245264
factory := new(mockFactory)
246-
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0))
265+
obs := New(new(mockLog), new(mockClient), &mockNotifier{}, factory, make([]string, 0), "")
247266

248267
// failing discovery
249268
obs.discovery.RESTClient().(*rest.RESTClient).Client = fakeClient.Client

0 commit comments

Comments
 (0)