1 package backend
2
3 import (
4 "fmt"
5 "sort"
6
7 kerrors "k8s.io/apimachinery/pkg/api/errors"
8 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9 "k8s.io/apimachinery/pkg/runtime/schema"
10 "k8s.io/klog"
11
12 "github.com/redhat-developer/odo/pkg/binding/asker"
13 "github.com/redhat-developer/odo/pkg/kclient"
14 "github.com/redhat-developer/odo/pkg/log"
15 "github.com/redhat-developer/odo/pkg/project"
16 )
17
18 type selectWorkloadStep int
19
20 const (
21 step_select_kind selectWorkloadStep = iota
22 step_select_name
23 step_selected
24 )
25
26
27 type InteractiveBackend struct {
28 askerClient asker.Asker
29 projectClient project.Client
30 kubernetesClient kclient.ClientInterface
31 }
32
33 var _ AddBindingBackend = (*InteractiveBackend)(nil)
34
35 func NewInteractiveBackend(askerClient asker.Asker, projectClient project.Client, kubernetesClient kclient.ClientInterface) *InteractiveBackend {
36 return &InteractiveBackend{
37 askerClient: askerClient,
38 projectClient: projectClient,
39 kubernetesClient: kubernetesClient,
40 }
41 }
42
43 func (o *InteractiveBackend) Validate(_ map[string]string, _ bool) error {
44 return nil
45 }
46
47 func (o *InteractiveBackend) SelectWorkloadInstance(_ string) (string, schema.GroupVersionKind, error) {
48
49 step := step_select_kind
50 var selectedGVK schema.GroupVersionKind
51 var selectedName string
52 loop:
53 for {
54 switch step {
55 case step_select_kind:
56 options, allWorkloadsKinds, err := o.kubernetesClient.GetWorkloadKinds()
57 if err != nil {
58 return "", schema.GroupVersionKind{}, err
59 }
60 i, err := o.askerClient.SelectWorkloadResource(options)
61 if err != nil {
62 return "", schema.GroupVersionKind{}, err
63 }
64 selectedGVK = allWorkloadsKinds[i]
65 step++
66
67 case step_select_name:
68
69 gvr, err := o.kubernetesClient.GetGVRFromGVK(selectedGVK)
70 if err != nil {
71 return "", schema.GroupVersionKind{}, err
72 }
73 resourceList, err := o.kubernetesClient.ListDynamicResources("", gvr, "")
74 if err != nil {
75 return "", schema.GroupVersionKind{}, err
76 }
77
78
79 names := make([]string, 0, len(resourceList.Items))
80 for _, resource := range resourceList.Items {
81 names = append(names, resource.GetName())
82 }
83 var back bool
84 back, selectedName, err = o.askerClient.SelectWorkloadResourceName(names)
85 if err != nil {
86 return "", schema.GroupVersionKind{}, err
87 }
88 if back {
89 step--
90 } else {
91 step++
92 }
93
94 case step_selected:
95 break loop
96 }
97 }
98
99
100 var err error
101 if selectedName == "" {
102 selectedName, err = o.askerClient.AskWorkloadResourceName()
103 if err != nil {
104 return "", schema.GroupVersionKind{}, err
105 }
106 }
107 return selectedName, selectedGVK, nil
108 }
109
110
111
112
113
114 func (o *InteractiveBackend) SelectNamespace(_ map[string]string) (string, error) {
115 option, err := o.askerClient.SelectNamespaceListOption()
116 if err != nil {
117 return "", err
118 }
119
120 switch option {
121 case asker.CurrentNamespace:
122 return "", nil
123 case asker.AllAccessibleNamespaces:
124 klog.V(2).Infof("Listing all projects/namespaces...")
125 var nsList []string
126 nsList, err = o.getAllNamespaces()
127 if err != nil {
128 return "", err
129 }
130 sort.Strings(nsList)
131 klog.V(4).Infof("all accessible namespaces: %v", nsList)
132 if len(nsList) == 0 {
133
134 return o.askerClient.AskNamespace()
135 }
136
137 return o.askerClient.SelectNamespace(nsList)
138 default:
139 return "", fmt.Errorf("unknown namespace list option: %d", option)
140 }
141 }
142
143 func (o *InteractiveBackend) SelectServiceInstance(_ string, serviceMap map[string]unstructured.Unstructured) (string, error) {
144 var options []string
145 for name := range serviceMap {
146 options = append(options, name)
147 }
148 return o.askerClient.AskServiceInstance(options)
149 }
150
151 func (o *InteractiveBackend) AskBindingName(defaultName string, _ map[string]string) (string, error) {
152 return o.askerClient.AskServiceBindingName(defaultName)
153 }
154
155 func (o *InteractiveBackend) AskBindAsFiles(_ map[string]string) (bool, error) {
156 return o.askerClient.AskBindAsFiles()
157 }
158
159 func (o *InteractiveBackend) AskNamingStrategy(_ map[string]string) (string, error) {
160 namingStrategy, err := o.askerClient.SelectNamingStrategy()
161 if err != nil {
162 return "", err
163 }
164 if namingStrategy == asker.NamingStrategyCustom {
165 return o.askerClient.AskNamingStrategy()
166 }
167 return namingStrategy, nil
168 }
169
170 func (o *InteractiveBackend) SelectCreationOptions(flags map[string]string) ([]asker.CreationOption, error) {
171 return o.askerClient.SelectCreationOptions()
172 }
173
174 func (o *InteractiveBackend) AskOutputFilePath(flags map[string]string, defaultValue string) (string, error) {
175 return o.askerClient.AskOutputFilePath(defaultValue)
176 }
177
178 func (o *InteractiveBackend) getAllNamespaces() ([]string, error) {
179 accessibleNsList, err := o.projectClient.List()
180 if err != nil {
181 klog.V(2).Infof("Failed to list namespaces/projects: %v", err)
182 if kerrors.IsForbidden(err) {
183
184
185 log.Warningf("Failed to list namespaces/projects: %v", err)
186 return nil, nil
187 }
188 return nil, err
189 }
190 nsList := make([]string, 0, len(accessibleNsList.Items))
191 for _, ns := range accessibleNsList.Items {
192 nsList = append(nsList, ns.Name)
193 }
194 return nsList, nil
195 }
196
View as plain text