sigs.k8s.io/controller-runtime@v0.18.2/pkg/internal/testing/controlplane/kubectl.go (about) 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 }