...

Source file src/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/kubectl.go

Documentation: sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controlplane
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"net/url"
    24  	"os/exec"
    25  
    26  	"k8s.io/client-go/rest"
    27  	"k8s.io/client-go/tools/clientcmd"
    28  	kcapi "k8s.io/client-go/tools/clientcmd/api"
    29  
    30  	"sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
    31  )
    32  
    33  const (
    34  	envtestName = "envtest"
    35  )
    36  
    37  // KubeConfigFromREST reverse-engineers a kubeconfig file from a rest.Config.
    38  // The options are tailored towards the rest.Configs we generate, so they're
    39  // not broadly applicable.
    40  //
    41  // This is not intended to be exposed beyond internal for the above reasons.
    42  func KubeConfigFromREST(cfg *rest.Config) ([]byte, error) {
    43  	kubeConfig := kcapi.NewConfig()
    44  	protocol := "https"
    45  	if !rest.IsConfigTransportTLS(*cfg) {
    46  		protocol = "http"
    47  	}
    48  
    49  	// cfg.Host is a URL, so we need to parse it so we can properly append the API path
    50  	baseURL, err := url.Parse(cfg.Host)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("unable to interpret config's host value as a URL: %w", err)
    53  	}
    54  
    55  	kubeConfig.Clusters[envtestName] = &kcapi.Cluster{
    56  		// TODO(directxman12): if client-go ever decides to expose defaultServerUrlFor(config),
    57  		// we can just use that.  Note that this is not the same as the public DefaultServerURL,
    58  		// which requires us to pass a bunch of stuff in manually.
    59  		Server:                   (&url.URL{Scheme: protocol, Host: baseURL.Host, Path: cfg.APIPath}).String(),
    60  		CertificateAuthorityData: cfg.CAData,
    61  	}
    62  	kubeConfig.AuthInfos[envtestName] = &kcapi.AuthInfo{
    63  		// try to cover all auth strategies that aren't plugins
    64  		ClientCertificateData: cfg.CertData,
    65  		ClientKeyData:         cfg.KeyData,
    66  		Token:                 cfg.BearerToken,
    67  		Username:              cfg.Username,
    68  		Password:              cfg.Password,
    69  	}
    70  	kcCtx := kcapi.NewContext()
    71  	kcCtx.Cluster = envtestName
    72  	kcCtx.AuthInfo = envtestName
    73  	kubeConfig.Contexts[envtestName] = kcCtx
    74  	kubeConfig.CurrentContext = envtestName
    75  
    76  	contents, err := clientcmd.Write(*kubeConfig)
    77  	if err != nil {
    78  		return nil, fmt.Errorf("unable to serialize kubeconfig file: %w", err)
    79  	}
    80  	return contents, nil
    81  }
    82  
    83  // KubeCtl is a wrapper around the kubectl binary.
    84  type KubeCtl struct {
    85  	// Path where the kubectl binary can be found.
    86  	//
    87  	// If this is left empty, we will attempt to locate a binary, by checking for
    88  	// the TEST_ASSET_KUBECTL environment variable, and the default test assets
    89  	// directory. See the "Binaries" section above (in doc.go) for details.
    90  	Path string
    91  
    92  	// Opts can be used to configure additional flags which will be used each
    93  	// time the wrapped binary is called.
    94  	//
    95  	// For example, you might want to use this to set the URL of the APIServer to
    96  	// connect to.
    97  	Opts []string
    98  }
    99  
   100  // Run executes the wrapped binary with some preconfigured options and the
   101  // arguments given to this method. It returns Readers for the stdout and
   102  // stderr.
   103  func (k *KubeCtl) Run(args ...string) (stdout, stderr io.Reader, err error) {
   104  	if k.Path == "" {
   105  		k.Path = process.BinPathFinder("kubectl", "")
   106  	}
   107  
   108  	stdoutBuffer := &bytes.Buffer{}
   109  	stderrBuffer := &bytes.Buffer{}
   110  	allArgs := append(k.Opts, args...)
   111  
   112  	cmd := exec.Command(k.Path, allArgs...)
   113  	cmd.Stdout = stdoutBuffer
   114  	cmd.Stderr = stderrBuffer
   115  	cmd.SysProcAttr = process.GetSysProcAttr()
   116  
   117  	err = cmd.Run()
   118  
   119  	return stdoutBuffer, stderrBuffer, err
   120  }
   121  

View as plain text