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  }