...

Source file src/github.com/redhat-developer/odo/pkg/auth/login.go

Documentation: github.com/redhat-developer/odo/pkg/auth

     1  package auth
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  
     9  	"github.com/openshift/oc/pkg/cli/login"
    10  	kapierrors "k8s.io/apimachinery/pkg/api/errors"
    11  	"k8s.io/cli-runtime/pkg/genericclioptions"
    12  	"k8s.io/client-go/tools/clientcmd"
    13  
    14  	odolog "github.com/redhat-developer/odo/pkg/log"
    15  )
    16  
    17  type KubernetesClient struct{}
    18  
    19  var _ Client = (*KubernetesClient)(nil)
    20  
    21  func NewKubernetesClient() *KubernetesClient {
    22  	return &KubernetesClient{}
    23  }
    24  
    25  // Login takes care of authentication part and returns error, if any
    26  func (o KubernetesClient) Login(server, username, password, token, caAuth string, skipTLS bool) error {
    27  	// Here we are grabbing the stdout output and then
    28  	// throwing it through "copyAndFilter" in order to get
    29  	// a correctly filtered result from `odo login`
    30  	filteredReader, filteredWriter := io.Pipe()
    31  	go func() {
    32  		defer filteredWriter.Close()
    33  		_, _ = copyAndFilter(odolog.GetStdout(), filteredReader)
    34  	}()
    35  
    36  	a := login.LoginOptions{
    37  		Server:         server,
    38  		CommandName:    "oc",
    39  		CAFile:         caAuth,
    40  		InsecureTLS:    skipTLS,
    41  		Username:       username,
    42  		Password:       password,
    43  		Project:        "",
    44  		Token:          token,
    45  		PathOptions:    &clientcmd.PathOptions{GlobalFile: clientcmd.RecommendedHomeFile, EnvVar: clientcmd.RecommendedConfigPathEnvVar, ExplicitFileFlag: "config", LoadingRules: &clientcmd.ClientConfigLoadingRules{ExplicitPath: ""}},
    46  		RequestTimeout: 0,
    47  		IOStreams:      genericclioptions.IOStreams{Out: filteredWriter, In: os.Stdin, ErrOut: odolog.GetStderr()},
    48  	}
    49  
    50  	// initialize client-go client and read starting kubeconfig file
    51  
    52  	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
    53  	configOverrides := &clientcmd.ConfigOverrides{}
    54  	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
    55  
    56  	rawKubeConfig, _ := kubeConfig.RawConfig()
    57  
    58  	a.StartingKubeConfig = &rawKubeConfig
    59  
    60  	// if server URL is not given as argument, we will look for current context from kubeconfig file
    61  	if len(a.Server) == 0 {
    62  		if defaultContext, defaultContextExists := a.StartingKubeConfig.Contexts[a.StartingKubeConfig.CurrentContext]; defaultContextExists {
    63  			if cluster, exists := a.StartingKubeConfig.Clusters[defaultContext.Cluster]; exists {
    64  				a.Server = cluster.Server
    65  			}
    66  		}
    67  	}
    68  
    69  	// if defaultNamespace is not defined, we will look for current namespace from kubeconfig file if defined
    70  	if len(a.DefaultNamespace) == 0 {
    71  		if defaultContext, defaultContextExists := a.StartingKubeConfig.Contexts[a.StartingKubeConfig.CurrentContext]; defaultContextExists {
    72  			if len(defaultContext.Namespace) > 0 {
    73  				a.DefaultNamespace = defaultContext.Namespace
    74  			}
    75  		}
    76  	}
    77  
    78  	// 1. Say we're connecting
    79  	odolog.Info("Connecting to the OpenShift cluster\n")
    80  
    81  	// 2. Handle the error messages here. This is copied over from:
    82  	// https://github.com/openshift/origin/blob/master/pkg/oc/cli/login/login.go#L60
    83  	// as unauthorized errors are handled MANUALLY by oc.
    84  	if err := a.GatherInfo(); err != nil {
    85  		if kapierrors.IsUnauthorized(err) {
    86  			fmt.Println("Login failed (401 Unauthorized)")
    87  			fmt.Println("Verify you have provided correct credentials.")
    88  
    89  			if err, isStatusErr := err.(*kapierrors.StatusError); isStatusErr {
    90  				if details := err.Status().Details; details != nil {
    91  					for _, cause := range details.Causes {
    92  						fmt.Println(cause.Message)
    93  					}
    94  				}
    95  			}
    96  		}
    97  		return err
    98  	}
    99  
   100  	// 3. Correctly save the configuration
   101  	newFileCreated, err := a.SaveConfig()
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	// If a new file has been created, we output what to do next (obviously odo help). This is taken from:
   107  	// https://github.com/openshift/origin/blob/4c293b86b111d9aaeba7bb1e72ee57410652ae9d/pkg/oc/cli/login/login.go#L184
   108  	if newFileCreated {
   109  		odolog.Infof("\nWelcome! See '%s help' to get started.", a.CommandName)
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // copyAndFilter captures the output, filters it and then spits it back out to stdout.
   116  // Kindly taken from https://stackoverflow.com/questions/54570268/filtering-the-output-of-a-terminal-output-using-golang
   117  func copyAndFilter(w io.Writer, r io.Reader) ([]byte, error) {
   118  	var out []byte
   119  	buf := make([]byte, 1024)
   120  	for {
   121  		n, err := r.Read(buf[:])
   122  		if n > 0 {
   123  			d := buf[:n]
   124  			out = append(out, d...)
   125  			if _, e := w.Write(filteredInformation(d)); e != nil {
   126  				return out, e
   127  			}
   128  		}
   129  		if err != nil {
   130  			// Read returns io.EOF at the end of file, which is not an error for us
   131  			if err == io.EOF {
   132  				err = nil
   133  			}
   134  			return out, err
   135  		}
   136  	}
   137  }
   138  
   139  // filteredInformation takes a list of strings ([]byte), replaces them and spits it back out
   140  // This is used since we utilize `oc login` with odo and require certain strings to be filtered / changed
   141  // to their odo equivalent
   142  func filteredInformation(s []byte) []byte {
   143  
   144  	// List of strings to correctly filter
   145  	s = bytes.Replace(s, []byte("oc new-project"), []byte("odo create project"), -1)
   146  	s = bytes.Replace(s, []byte("oc project "), []byte("odo set project "), -1)
   147  	s = bytes.Replace(s, []byte("oc projects"), []byte("odo list project"), -1)
   148  
   149  	return s
   150  }
   151  

View as plain text