diff --git a/cmd/npv/app/dump.go b/cmd/npv/app/dump.go index 8d310ab..f97490f 100644 --- a/cmd/npv/app/dump.go +++ b/cmd/npv/app/dump.go @@ -24,6 +24,7 @@ var dumpCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { return runDump(context.Background(), cmd.OutOrStdout(), args[0]) }, + ValidArgsFunction: completePods, } func runDump(ctx context.Context, w io.Writer, name string) error { diff --git a/cmd/npv/app/helper.go b/cmd/npv/app/helper.go index b0d6963..e0025ac 100644 --- a/cmd/npv/app/helper.go +++ b/cmd/npv/app/helper.go @@ -121,6 +121,26 @@ func getPodIdentity(ctx context.Context, d *dynamic.DynamicClient, namespace, na return identity, nil } +func listRelevantPods(ctx context.Context, c *kubernetes.Clientset, namespace string) ([]corev1.Pod, error) { + pods, err := c.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + ret := make([]corev1.Pod, 0) + for _, p := range pods.Items { + // Skip non-relevant pods + if p.Spec.HostNetwork { + continue + } + if p.Status.Phase != corev1.PodRunning { + continue + } + ret = append(ret, p) + } + return ret, nil +} + // key: identity number // value: CiliumIdentity resource func getIdentityResourceMap(ctx context.Context, d *dynamic.DynamicClient) (map[int]*unstructured.Unstructured, error) { diff --git a/cmd/npv/app/helper_completion.go b/cmd/npv/app/helper_completion.go new file mode 100644 index 0000000..67df30c --- /dev/null +++ b/cmd/npv/app/helper_completion.go @@ -0,0 +1,48 @@ +package app + +import ( + "context" + + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func completeNamespaces(cmd *cobra.Command, args []string, toComplete string) (ret []string, directive cobra.ShellCompDirective) { + ret = make([]string, 0) + directive = cobra.ShellCompDirectiveNoFileComp + + clientset, _, err := createK8sClients() + if err != nil { + return + } + + nss, err := clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{}) + if err != nil { + return + } + + for _, ns := range nss.Items { + ret = append(ret, ns.Name) + } + return +} + +func completePods(cmd *cobra.Command, args []string, toComplete string) (ret []string, directive cobra.ShellCompDirective) { + ret = make([]string, 0) + directive = cobra.ShellCompDirectiveNoFileComp + + clientset, _, err := createK8sClients() + if err != nil { + return + } + + pods, err := listRelevantPods(context.Background(), clientset, rootOptions.namespace) + if err != nil { + return + } + + for _, p := range pods { + ret = append(ret, p.Name) + } + return +} diff --git a/cmd/npv/app/inspect.go b/cmd/npv/app/inspect.go index 1a442e2..1d8253f 100644 --- a/cmd/npv/app/inspect.go +++ b/cmd/npv/app/inspect.go @@ -33,6 +33,7 @@ var inspectCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { return runInspect(context.Background(), cmd.OutOrStdout(), args[0]) }, + ValidArgsFunction: completePods, } type policyEntryKey struct { diff --git a/cmd/npv/app/list.go b/cmd/npv/app/list.go index df58d1e..adb8b78 100644 --- a/cmd/npv/app/list.go +++ b/cmd/npv/app/list.go @@ -36,6 +36,7 @@ var listCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { return runList(context.Background(), cmd.OutOrStdout(), args[0]) }, + ValidArgsFunction: completePods, } type derivedFromEntry struct { diff --git a/cmd/npv/app/root.go b/cmd/npv/app/root.go index d46738f..9cd42dd 100644 --- a/cmd/npv/app/root.go +++ b/cmd/npv/app/root.go @@ -26,9 +26,12 @@ func init() { rootCmd.PersistentFlags().Uint16Var(&rootOptions.proxyPort, "proxy-port", 8080, "port number of the proxy endpoints") rootCmd.PersistentFlags().StringVarP(&rootOptions.output, "output", "o", OutputSimple, "output format") rootCmd.PersistentFlags().BoolVar(&rootOptions.noHeaders, "no-headers", false, "stop printing header") + rootCmd.RegisterFlagCompletionFunc("namespace", completeNamespaces) } -var rootCmd = &cobra.Command{} +var rootCmd = &cobra.Command{ + Use: "npv", +} func Execute() { if err := rootCmd.Execute(); err != nil { diff --git a/cmd/npv/app/summary.go b/cmd/npv/app/summary.go index 3dcc88a..cc598fe 100644 --- a/cmd/npv/app/summary.go +++ b/cmd/npv/app/summary.go @@ -7,8 +7,6 @@ import ( "strings" "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func init() { @@ -50,24 +48,16 @@ func runSummary(ctx context.Context, w io.Writer) error { } summary := make([]summaryEntry, 0) - pods, err := clientset.CoreV1().Pods(rootOptions.namespace).List(ctx, metav1.ListOptions{}) + pods, err := listRelevantPods(ctx, clientset, rootOptions.namespace) if err != nil { return err } - for _, p := range pods.Items { + for _, p := range pods { var entry summaryEntry entry.Namespace = p.Namespace entry.Name = p.Name - // Skip non-relevant pods - if p.Spec.HostNetwork { - continue - } - if p.Status.Phase != corev1.PodRunning { - continue - } - policies, err := queryPolicyMap(ctx, clientset, dynamicClient, rootOptions.namespace, p.Name) if err != nil { return err diff --git a/e2e/Makefile b/e2e/Makefile index 845a1b6..8d92e56 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -72,11 +72,19 @@ install-test-pod: install-policy-viewer: $(MAKE) -C ../ build PODNAME=$$(kubectl get po -l app=ubuntu -o name | cut -d'/' -f2); \ + kubectl cp testdata/onboard $${PODNAME}:/tmp/; \ + kubectl exec $${PODNAME} -- chmod +x /tmp/onboard; \ + kubectl exec $${PODNAME} -- /tmp/onboard; \ kubectl cp $(POLICY_VIEWER) $${PODNAME}:/tmp/; \ kubectl exec $${PODNAME} -- chmod +x /tmp/npv; \ kubectl cp $$(aqua which kubectl) $${PODNAME}:/tmp/; \ kubectl exec $${PODNAME} -- chmod +x /tmp/kubectl +.PHONY: pilot +pilot: + @PODNAME=$$(kubectl get po -l app=ubuntu -o name | cut -d'/' -f2); \ + kubectl exec -it $${PODNAME} -- bash + .PHONY: test test: go test -v -race . -ginkgo.v -ginkgo.fail-fast diff --git a/e2e/testdata/onboard b/e2e/testdata/onboard new file mode 100644 index 0000000..f3311e0 --- /dev/null +++ b/e2e/testdata/onboard @@ -0,0 +1,8 @@ +#!/bin/bash + +if ! grep boarded /root/.bashrc > /dev/null 2>&1; then + echo '. /usr/share/bash-completion/bash_completion' >> /root/.bashrc + echo 'export PATH=${PATH}:/tmp' >> /root/.bashrc + echo '. <(npv completion bash)' >> /root/.bashrc + echo '# boarded' >> /root/.bashrc +fi diff --git a/e2e/testdata/ubuntu.yaml b/e2e/testdata/ubuntu.yaml index 30d8f47..23dcd92 100644 --- a/e2e/testdata/ubuntu.yaml +++ b/e2e/testdata/ubuntu.yaml @@ -12,6 +12,7 @@ rules: - apiGroups: - "" resources: + - namespaces - pods verbs: - get @@ -58,9 +59,6 @@ spec: app: ubuntu spec: serviceAccountName: ubuntu - securityContext: - runAsUser: 1000 - runAsGroup: 1000 containers: - name: ubuntu args: