github.com/opendevstack/tailor@v1.3.5-0.20220119161809-cab064e60a67/pkg/cli/oc_client.go (about) 1 package cli 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os/exec" 8 "strings" 9 ) 10 11 type ClientApplier interface { 12 ClientProcessorExporter 13 ClientModifier 14 } 15 16 // ClientProcessorExporter allows to process templates and export resources. 17 type ClientProcessorExporter interface { 18 OcClientProcessor 19 OcClientExporter 20 } 21 22 // ClientModifier allows to delete and create/update resources. 23 type ClientModifier interface { 24 OcClientApplier 25 OcClientDeleter 26 } 27 28 // OcClientProcessor is a stop-gap solution only ... should have a better API. 29 type OcClientProcessor interface { 30 Process(args []string) ([]byte, []byte, error) 31 } 32 33 // OcClientExporter allows to export resources. 34 type OcClientExporter interface { 35 Export(target string, label string) ([]byte, error) 36 } 37 38 // OcClientDeleter allows to delete a resource. 39 type OcClientDeleter interface { 40 Delete(kind string, name string) ([]byte, error) 41 } 42 43 // OcClientApplier allows to create/update a resource. 44 type OcClientApplier interface { 45 Apply(config string, selector string) ([]byte, error) 46 } 47 48 // OcClientVersioner allows to retrieve the OpenShift version.. 49 type OcClientVersioner interface { 50 Version() ([]byte, []byte, error) 51 } 52 53 // OcClient is a wrapper around the "oc" binary (client). 54 type OcClient struct { 55 namespace string 56 } 57 58 // NewOcClient creates a new ocClient. 59 func NewOcClient(namespace string) *OcClient { 60 return &OcClient{namespace: namespace} 61 } 62 63 // Version returns the output of "ov versiopn". 64 func (c *OcClient) Version() ([]byte, []byte, error) { 65 cmd := c.execPlainOcCmd([]string{"version"}) 66 return c.runCmd(cmd) 67 68 } 69 70 // CurrentProject returns the currently active project name (namespace). 71 func (c *OcClient) CurrentProject() (string, error) { 72 cmd := c.execPlainOcCmd([]string{"project", "--short"}) 73 n, err := cmd.CombinedOutput() 74 return strings.TrimSpace(string(n)), err 75 } 76 77 // CheckProjectExists returns true if the given project (namespace) exists. 78 func (c *OcClient) CheckProjectExists(p string) (bool, error) { 79 cmd := c.execPlainOcCmd([]string{"project", p, "--short"}) 80 _, err := cmd.CombinedOutput() 81 return err == nil, err 82 } 83 84 // CheckLoggedIn returns true if the given project (namespace) exists. 85 func (c *OcClient) CheckLoggedIn() (bool, error) { 86 cmd := exec.Command(ocBinary, "whoami") 87 _, err := cmd.CombinedOutput() 88 return err == nil, err 89 } 90 91 // Process processes an OpenShift template. 92 // The API is just a stop-gap solution and will be better in the future. 93 func (c *OcClient) Process(args []string) ([]byte, []byte, error) { 94 processArgs := append([]string{"process"}, args...) 95 cmd := c.execPlainOcCmd(processArgs) 96 return c.runCmd(cmd) 97 } 98 99 // Export exports resources from OpenShift as a template. 100 func (c *OcClient) Export(target string, label string) ([]byte, error) { 101 args := []string{"get", target, "--output=yaml"} 102 cmd := c.execOcCmd( 103 args, 104 c.namespace, 105 label, 106 ) 107 outBytes, errBytes, err := c.runCmd(cmd) 108 109 if err != nil { 110 ret := string(errBytes) 111 112 if strings.Contains(ret, "no resources found") { 113 return []byte{}, nil 114 } 115 116 return []byte{}, fmt.Errorf( 117 "Failed to export %s resources.\n"+ 118 "%s\n", 119 target, 120 ret, 121 ) 122 } 123 124 return outBytes, nil 125 } 126 127 // Apply applies given resource configuration. 128 func (c *OcClient) Apply(config string, selector string) ([]byte, error) { 129 args := []string{"apply", "-f", "-"} 130 cmd := c.execOcCmd( 131 args, 132 c.namespace, 133 selector, 134 ) 135 stdin, err := cmd.StdinPipe() 136 if err != nil { 137 return nil, err 138 } 139 go func() { 140 defer stdin.Close() 141 _, _ = io.WriteString(stdin, config) 142 }() 143 _, errBytes, err := c.runCmd(cmd) 144 return errBytes, err 145 } 146 147 // Delete deletes given resource. 148 func (c *OcClient) Delete(kind string, name string) ([]byte, error) { 149 args := []string{"delete", kind, name} 150 cmd := c.execOcCmd( 151 args, 152 c.namespace, 153 "", // empty as name and selector is not allowed 154 ) 155 _, errBytes, err := c.runCmd(cmd) 156 return errBytes, err 157 } 158 159 func (c *OcClient) execOcCmd(args []string, namespace string, selector string) *exec.Cmd { 160 if len(namespace) > 0 { 161 args = append(args, "--namespace="+namespace) 162 } 163 if len(selector) > 0 { 164 args = append(args, "--selector="+selector) 165 } 166 return c.execPlainOcCmd(args) 167 } 168 169 func (c *OcClient) execPlainOcCmd(args []string) *exec.Cmd { 170 return c.execCmd(ocBinary, args) 171 } 172 173 func (c *OcClient) execCmd(executable string, args []string) *exec.Cmd { 174 if verbose { 175 PrintBluef("--> %s\n", executable+" "+strings.Join(args, " ")) 176 } 177 return exec.Command(executable, args...) 178 } 179 180 func (c *OcClient) runCmd(cmd *exec.Cmd) (outBytes, errBytes []byte, err error) { 181 var stdout, stderr bytes.Buffer 182 cmd.Stdout = &stdout 183 cmd.Stderr = &stderr 184 err = cmd.Run() 185 outBytes = stdout.Bytes() 186 errBytes = stderr.Bytes() 187 return outBytes, errBytes, err 188 }