...

Source file src/github.com/redhat-developer/odo/pkg/binding/backend/interactive.go

Documentation: github.com/redhat-developer/odo/pkg/binding/backend

     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  // InteractiveBackend is a backend that will ask information interactively using the `asker` package
    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  			// Get the resources of this kind
    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  			// Ask to select the name of the resource
    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  	// Ask the name if DOES NOT EXIST is selected
   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  // SelectNamespace prompts users to select the namespace which services instances should be listed from.
   111  // If they choose all the namespaces they have access to, it attempts to get the list of accessible namespaces in the cluster,
   112  // from which the user can select one.
   113  // If the list is empty (e.g. because of permission-related issues), the user is prompted to manually provide a namespace.
   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  			// User needs to provide a namespace
   134  			return o.askerClient.AskNamespace()
   135  		}
   136  		// Let users select a namespace from the list
   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  			// If status is forbidden, this might be an RBAC error due to user not having permission to list namespaces.
   184  			// In this case, user will need to manually specify the namespace.
   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