github.com/jaylevin/jenkins-library@v1.230.4/cmd/xsDeploy_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/SAP/jenkins-library/pkg/mock"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  type FileUtilsMock struct {
    17  	*mock.FilesMock
    18  	copiedFiles []string
    19  }
    20  
    21  func (f *FileUtilsMock) FileExists(path string) (bool, error) {
    22  	return path == "dummy.mtar" || path == ".xs_session", nil
    23  }
    24  
    25  func (f *FileUtilsMock) Copy(src, dest string) (int64, error) {
    26  	f.copiedFiles = append(f.copiedFiles, fmt.Sprintf("%s->%s", src, dest))
    27  	return 0, nil
    28  }
    29  
    30  func TestDeploy(t *testing.T) {
    31  	myXsDeployOptions := xsDeployOptions{
    32  		APIURL:                "https://example.org:12345",
    33  		Username:              "me",
    34  		Password:              "secretPassword",
    35  		Org:                   "myOrg",
    36  		Space:                 "mySpace",
    37  		LoginOpts:             "--skip-ssl-validation",
    38  		DeployOpts:            "--dummy-deploy-opts",
    39  		XsSessionFile:         ".xs_session",
    40  		Mode:                  "DEPLOY",
    41  		Action:                "NONE",
    42  		MtaPath:               "dummy.mtar",
    43  		OperationIDLogPattern: `^.*xs bg-deploy -i (.*) -a.*$`,
    44  	}
    45  
    46  	s := mock.ShellMockRunner{}
    47  
    48  	var removedFiles []string
    49  
    50  	cpeOut := xsDeployCommonPipelineEnvironment{}
    51  	fileUtilsMock := FileUtilsMock{}
    52  
    53  	fRemove := func(path string) error {
    54  		removedFiles = append(removedFiles, path)
    55  		return nil
    56  	}
    57  
    58  	var stdout string
    59  
    60  	t.Run("Standard deploy succeeds", func(t *testing.T) {
    61  
    62  		defer func() {
    63  			fileUtilsMock.copiedFiles = nil
    64  			removedFiles = nil
    65  			s.Calls = nil
    66  			stdout = ""
    67  		}()
    68  
    69  		rStdout, wStdout := io.Pipe()
    70  
    71  		var wg sync.WaitGroup
    72  		wg.Add(1)
    73  
    74  		go func() {
    75  			buf := new(bytes.Buffer)
    76  			io.Copy(buf, rStdout)
    77  			stdout = buf.String()
    78  			wg.Done()
    79  		}()
    80  
    81  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, wStdout)
    82  
    83  		wStdout.Close()
    84  		wg.Wait()
    85  
    86  		assert.NoError(t, e)
    87  
    88  		t.Run("Standard checks", func(t *testing.T) {
    89  			// Contains --> we do not check for the shebang
    90  			assert.Contains(t, s.Calls[0], "xs login -a https://example.org:12345 -u me -p 'secretPassword' -o myOrg -s mySpace --skip-ssl-validation")
    91  			assert.Contains(t, s.Calls[1], "xs deploy dummy.mtar --dummy-deploy-opts")
    92  			assert.Contains(t, s.Calls[2], "xs logout")
    93  			assert.Len(t, s.Calls, 3)
    94  
    95  			// xs session file needs to be removed at end during a normal deployment
    96  			assert.Len(t, removedFiles, 1)
    97  			assert.Contains(t, removedFiles, ".xs_session")
    98  
    99  			assert.Len(t, fileUtilsMock.copiedFiles, 2)
   100  			// We copy the xs session file to the workspace in order to be able to use the file later.
   101  			// This happens directly after login
   102  			// We copy the xs session file from the workspace to the home folder in order to be able to
   103  			// use that file. This is important in case we rely on a login which happened e
   104  			assert.Contains(t, fileUtilsMock.copiedFiles[0], "/.xs_session->.xs_session")
   105  			assert.Contains(t, fileUtilsMock.copiedFiles[1], ".xs_session->")
   106  			assert.Contains(t, fileUtilsMock.copiedFiles[1], "/.xs_session")
   107  		})
   108  
   109  		t.Run("Password not exposed", func(t *testing.T) {
   110  			assert.NotEmpty(t, stdout)
   111  			assert.NotContains(t, stdout, myXsDeployOptions.Password)
   112  		})
   113  	})
   114  
   115  	t.Run("Standard deploy fails, deployable missing", func(t *testing.T) {
   116  
   117  		defer func() {
   118  			fileUtilsMock.copiedFiles = nil
   119  			removedFiles = nil
   120  			s.Calls = nil
   121  		}()
   122  
   123  		oldMtaPath := myXsDeployOptions.MtaPath
   124  
   125  		defer func() {
   126  			myXsDeployOptions.MtaPath = oldMtaPath
   127  		}()
   128  
   129  		// this file is not denoted in the file exists mock
   130  		myXsDeployOptions.MtaPath = "doesNotExist"
   131  
   132  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   133  		assert.EqualError(t, e, "Deployable 'doesNotExist' does not exist")
   134  	})
   135  
   136  	t.Run("Standard deploy fails, action provided", func(t *testing.T) {
   137  
   138  		defer func() {
   139  			fileUtilsMock.copiedFiles = nil
   140  			removedFiles = nil
   141  			s.Calls = nil
   142  		}()
   143  
   144  		myXsDeployOptions.Action = "RETRY"
   145  		defer func() {
   146  			myXsDeployOptions.Action = "NONE"
   147  		}()
   148  
   149  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   150  		assert.EqualError(t, e, "Cannot perform action 'RETRY' in mode 'DEPLOY'. Only action 'NONE' is allowed.")
   151  	})
   152  
   153  	t.Run("Standard deploy fails, error from underlying process", func(t *testing.T) {
   154  
   155  		defer func() {
   156  			fileUtilsMock.copiedFiles = nil
   157  			removedFiles = nil
   158  			s.Calls = nil
   159  			s.ShouldFailOnCommand = nil
   160  		}()
   161  
   162  		s.ShouldFailOnCommand = map[string]error{"#!/bin/bash\nxs login -a https://example.org:12345 -u me -p 'secretPassword' -o myOrg -s mySpace --skip-ssl-validation\n": errors.New("Error from underlying process")}
   163  
   164  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   165  		assert.EqualError(t, e, "Error from underlying process")
   166  	})
   167  
   168  	t.Run("BG deploy succeeds", func(t *testing.T) {
   169  
   170  		defer func() {
   171  			fileUtilsMock.copiedFiles = nil
   172  			removedFiles = nil
   173  			s.Calls = nil
   174  			s.StdoutReturn = make(map[string]string)
   175  		}()
   176  
   177  		s.StdoutReturn = make(map[string]string)
   178  		s.StdoutReturn[".*xs bg-deploy.*"] = "Use \"xs bg-deploy -i 1234 -a resume\" to resume the process.\n"
   179  
   180  		oldMode := myXsDeployOptions.Mode
   181  
   182  		defer func() {
   183  			myXsDeployOptions.Mode = oldMode
   184  		}()
   185  
   186  		myXsDeployOptions.Mode = "BG_DEPLOY"
   187  
   188  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   189  
   190  		if assert.NoError(t, e) {
   191  			if assert.Len(t, s.Calls, 2) { // There are two entries --> no logout in this case.
   192  				assert.Contains(t, s.Calls[0], "xs login")
   193  				assert.Contains(t, s.Calls[1], "xs bg-deploy dummy.mtar --dummy-deploy-opts")
   194  			}
   195  		}
   196  	})
   197  
   198  	t.Run("BG deploy fails, missing operationID", func(t *testing.T) {
   199  
   200  		s.StdoutReturn = make(map[string]string)
   201  		s.StdoutReturn[".*bg_deploy.*"] = "There is no operationID ...\n"
   202  		defer func() {
   203  			fileUtilsMock.copiedFiles = nil
   204  			removedFiles = nil
   205  			s.Calls = nil
   206  			s.StdoutReturn = make(map[string]string)
   207  		}()
   208  
   209  		oldMode := myXsDeployOptions.Mode
   210  
   211  		defer func() {
   212  			myXsDeployOptions.Mode = oldMode
   213  		}()
   214  
   215  		myXsDeployOptions.Mode = "BG_DEPLOY"
   216  
   217  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   218  		assert.EqualError(t, e, "No operationID found")
   219  	})
   220  
   221  	t.Run("BG deploy abort succeeds", func(t *testing.T) {
   222  
   223  		defer func() {
   224  			fileUtilsMock.copiedFiles = nil
   225  			removedFiles = nil
   226  			s.Calls = nil
   227  		}()
   228  
   229  		oldMode := myXsDeployOptions.Mode
   230  		oldAction := myXsDeployOptions.Action
   231  
   232  		defer func() {
   233  			myXsDeployOptions.Mode = oldMode
   234  			myXsDeployOptions.Action = oldAction
   235  			myXsDeployOptions.OperationID = ""
   236  		}()
   237  
   238  		myXsDeployOptions.Mode = "BG_DEPLOY"
   239  		myXsDeployOptions.Action = "ABORT"
   240  		myXsDeployOptions.OperationID = "12345"
   241  
   242  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   243  
   244  		if assert.NoError(t, e) {
   245  			if assert.Len(t, s.Calls, 2) { // There is no login --> we have two calls
   246  				assert.Contains(t, s.Calls[0], "xs bg-deploy -i 12345 -a abort")
   247  				assert.Contains(t, s.Calls[1], "xs logout")
   248  			}
   249  
   250  		}
   251  	})
   252  
   253  	t.Run("BG deploy abort fails due to missing operationId", func(t *testing.T) {
   254  
   255  		defer func() {
   256  			fileUtilsMock.copiedFiles = nil
   257  			removedFiles = nil
   258  			s.Calls = nil
   259  		}()
   260  
   261  		oldMode := myXsDeployOptions.Mode
   262  		oldAction := myXsDeployOptions.Action
   263  
   264  		defer func() {
   265  			myXsDeployOptions.Mode = oldMode
   266  			myXsDeployOptions.Action = oldAction
   267  		}()
   268  
   269  		myXsDeployOptions.Mode = "BG_DEPLOY"
   270  		myXsDeployOptions.Action = "ABORT"
   271  
   272  		e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard)
   273  		assert.EqualError(t, e, "OperationID was not provided. This is required for action 'ABORT'.")
   274  	})
   275  }
   276  
   277  func TestRetrieveOperationID(t *testing.T) {
   278  	operationID := retrieveOperationID(`
   279  	Uploading 1 files:
   280          myFolder/dummy.mtar
   281  	File upload finished
   282  
   283  	Detected MTA schema version: "3.1.0"
   284  	Detected deploy target as "myOrg mySpace"
   285  	Detected deployed MTA with ID "my_mta" and version "0.0.1"
   286  	Deployed MTA color: blue
   287  	New MTA color: green
   288  	Detected new MTA version: "0.0.1"
   289  	Deployed MTA version: 0.0.1
   290  	Service "xxx" is not modified and will not be updated
   291  	Creating application "db-green" from MTA module "xx"...
   292  	Uploading application "xx-green"...
   293  	Staging application "xx-green"...
   294  	Application "xx-green" staged
   295  	Executing task "deploy" on application "xx-green"...
   296  	Task execution status: succeeded
   297  	Process has entered validation phase. After testing your new deployment you can resume or abort the process.
   298  	Use "xs bg-deploy -i 1234 -a resume" to resume the process.
   299  	Use "xs bg-deploy -i 1234 -a abort" to abort the process.
   300  	Hint: Use the '--no-confirm' option of the bg-deploy command to skip this phase.
   301  	`, `^.*xs bg-deploy -i (.*) -a.*$`)
   302  
   303  	assert.Equal(t, "1234", operationID)
   304  }