1 package cmd
2
3 import (
4 "bufio"
5 "context"
6 "errors"
7 "fmt"
8 "io"
9 "os"
10 "strings"
11
12 "github.com/linkerd/linkerd2/pkg/k8s"
13 "github.com/linkerd/linkerd2/pkg/k8s/resource"
14 log "github.com/sirupsen/logrus"
15 "github.com/spf13/cobra"
16 corev1 "k8s.io/api/core/v1"
17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18 "k8s.io/apimachinery/pkg/labels"
19 "k8s.io/apimachinery/pkg/selection"
20 yamlDecoder "k8s.io/apimachinery/pkg/util/yaml"
21 "k8s.io/client-go/tools/clientcmd"
22 "sigs.k8s.io/yaml"
23 )
24
25 var (
26
27 DefaultDockerRegistry = "cr.l5d.io/linkerd"
28 )
29
30
31
32 func GetDefaultNamespace(kubeconfigPath, kubeContext string) string {
33 rules := clientcmd.NewDefaultClientConfigLoadingRules()
34
35 if kubeconfigPath != "" {
36 rules.ExplicitPath = kubeconfigPath
37 }
38
39 overrides := &clientcmd.ConfigOverrides{CurrentContext: kubeContext}
40 kubeCfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides)
41 ns, _, err := kubeCfg.Namespace()
42
43 if err != nil {
44 log.Warnf(`could not set namespace from kubectl context, using 'default' namespace: %s
45 ensure the KUBECONFIG path %s is valid`, err, kubeconfigPath)
46 return corev1.NamespaceDefault
47 }
48
49 return ns
50 }
51
52
53
54 func Uninstall(ctx context.Context, k8sAPI *k8s.KubernetesAPI, selector string) error {
55 resources, err := resource.FetchKubernetesResources(ctx, k8sAPI,
56 metav1.ListOptions{LabelSelector: selector},
57 )
58 if err != nil {
59 return err
60 }
61
62 if len(resources) == 0 {
63 return errors.New("No resources found to uninstall")
64 }
65 for _, r := range resources {
66 if err := r.RenderResource(os.Stdout); err != nil {
67 return fmt.Errorf("error rendering Kubernetes resource: %w", err)
68 }
69 }
70 return nil
71 }
72
73
74
75
76
77 func Prune(ctx context.Context, k8sAPI *k8s.KubernetesAPI, expectedManifests string, selector string) error {
78 expectedResources := []resource.Kubernetes{}
79 reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(strings.NewReader(expectedManifests), 4096))
80 for {
81 manifest, err := reader.Read()
82 if err != nil {
83 if errors.Is(err, io.EOF) {
84 break
85 }
86 return err
87 }
88 resource := resource.Kubernetes{}
89 err = yaml.Unmarshal(manifest, &resource)
90 if err != nil {
91 fmt.Fprintf(os.Stderr, "error parsing manifest: %s", manifest)
92 os.Exit(1)
93 }
94 expectedResources = append(expectedResources, resource)
95 }
96
97 listOptions := metav1.ListOptions{
98 LabelSelector: selector,
99 }
100 resources, err := resource.FetchPrunableResources(ctx, k8sAPI, metav1.NamespaceAll, listOptions)
101 if err != nil {
102 fmt.Fprintf(os.Stderr, "error fetching resources: %s\n", err)
103 os.Exit(1)
104 }
105
106 for _, resource := range resources {
107
108
109 if !resourceListContains(expectedResources, resource) {
110 if err = resource.RenderResource(os.Stdout); err != nil {
111 return fmt.Errorf("error rendering Kubernetes resource: %w\n", err)
112 }
113 }
114 }
115 return nil
116 }
117
118 func resourceListContains(list []resource.Kubernetes, a resource.Kubernetes) bool {
119 for _, r := range list {
120 if resourceEquals(a, r) {
121 return true
122 }
123 }
124 return false
125 }
126
127 func resourceEquals(a resource.Kubernetes, b resource.Kubernetes) bool {
128 return a.GroupVersionKind().GroupKind() == b.GroupVersionKind().GroupKind() &&
129 a.GetName() == b.GetName() &&
130 a.GetNamespace() == b.GetNamespace()
131 }
132
133
134
135 func ConfigureNamespaceFlagCompletion(
136 cmd *cobra.Command,
137 flagNames []string,
138 kubeconfigPath string,
139 impersonate string,
140 impersonateGroup []string,
141 kubeContext string,
142 ) {
143 for _, flagName := range flagNames {
144 cmd.RegisterFlagCompletionFunc(flagName,
145 func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
146 k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
147 if err != nil {
148 return nil, cobra.ShellCompDirectiveError
149 }
150
151 cc := k8s.NewCommandCompletion(k8sAPI, "")
152 results, err := cc.Complete([]string{k8s.Namespace}, toComplete)
153 if err != nil {
154 return nil, cobra.ShellCompDirectiveError
155 }
156
157 return results, cobra.ShellCompDirectiveDefault
158 })
159 }
160 }
161
162
163
164 func ConfigureOutputFlagCompletion(cmd *cobra.Command) {
165 cmd.RegisterFlagCompletionFunc("output",
166 func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
167 return []string{"basic", "json", "short", "table"}, cobra.ShellCompDirectiveDefault
168 })
169 }
170
171
172
173 func ConfigureKubeContextFlagCompletion(cmd *cobra.Command, kubeconfigPath string) {
174 cmd.RegisterFlagCompletionFunc("context",
175 func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
176 rules := clientcmd.NewDefaultClientConfigLoadingRules()
177 rules.ExplicitPath = kubeconfigPath
178 loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, &clientcmd.ConfigOverrides{})
179 config, err := loader.RawConfig()
180 if err != nil {
181 return nil, cobra.ShellCompDirectiveError
182 }
183
184 suggestions := []string{}
185 uniqContexts := map[string]struct{}{}
186 for ctxName := range config.Contexts {
187 if strings.HasPrefix(ctxName, toComplete) {
188 if _, ok := uniqContexts[ctxName]; !ok {
189 suggestions = append(suggestions, ctxName)
190 uniqContexts[ctxName] = struct{}{}
191 }
192 }
193 }
194
195 return suggestions, cobra.ShellCompDirectiveDefault
196 })
197 }
198
199
200
201
202
203 func GetLabelSelector(labelKey string, labelValues ...string) (string, error) {
204 selectionOp := selection.In
205 if len(labelValues) < 1 {
206 selectionOp = selection.Exists
207 }
208
209 labelRequirement, err := labels.NewRequirement(labelKey, selectionOp, labelValues)
210 if err != nil {
211 return "", err
212 }
213
214 selector := labels.NewSelector().Add(*labelRequirement)
215 return selector.String(), nil
216 }
217
218
219 func RegistryOverride(image, newRegistry string) string {
220 if image == "" {
221 return image
222 }
223 registry := newRegistry
224 if registry != "" && !strings.HasSuffix(registry, "/") {
225 registry += "/"
226 }
227 imageName := image
228 if strings.Contains(image, "/") {
229 imageName = image[strings.LastIndex(image, "/")+1:]
230 }
231 return registry + imageName
232 }
233
View as plain text