github.com/cloudfoundry-incubator/windows-utilities-tests@v0.11.1-0.20230315194243-a2ce46b74d8a/utils_test.go (about)

     1  package wuts_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"log"
    10  	"math/rand"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  	"text/template"
    16  	"time"
    17  
    18  	. "github.com/cloudfoundry-incubator/windows-utilities-tests/templates"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  	. "github.com/onsi/gomega/gbytes"
    22  	. "github.com/onsi/gomega/gexec"
    23  	"gopkg.in/yaml.v2"
    24  )
    25  
    26  const BOSH_TIMEOUT = 90 * time.Minute
    27  
    28  type ManifestProperties struct {
    29  	DeploymentName  string
    30  	ReleaseName     string
    31  	AZ              string
    32  	VmType          string
    33  	VmExtensions    string
    34  	Network         string
    35  	StemcellOS      string
    36  	StemcellVersion string
    37  	WinUtilVersion  string
    38  	WutsVersion     string
    39  }
    40  
    41  type Config struct {
    42  	Bosh struct {
    43  		CaCert       string `json:"ca_cert"`
    44  		Client       string `json:"client"`
    45  		ClientSecret string `json:"client_secret"`
    46  		Target       string `json:"target"`
    47  		SSHTunnelIP  string `json:"ssh_tunnel_ip"`
    48  		GwPrivateKey string `json:"gw_private_key"`
    49  		GwUser       string `json:"gw_user"`
    50  	} `json:"bosh"`
    51  	StemcellPath         string `json:"stemcell_path"`
    52  	WindowsUtilitiesPath string `json:"windows_utilities_path"`
    53  	StemcellOS           string `json:"stemcell_os"`
    54  	Az                   string `json:"az"`
    55  	VmType               string `json:"vm_type"`
    56  	VmExtensions         string `json:"vm_extensions"`
    57  	Network              string `json:"network"`
    58  	SkipCleanup          bool   `json:"skip_cleanup"`
    59  	SkipCleanupOnRDPFail bool   `json:"skip_cleanup_on_rdp_fail"`
    60  }
    61  
    62  func NewConfig() (*Config, error) {
    63  	configFilePath := os.Getenv("CONFIG_JSON")
    64  	if configFilePath == "" {
    65  		return nil, fmt.Errorf("invalid config file path: %v", configFilePath)
    66  	}
    67  	body, err := ioutil.ReadFile(configFilePath)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("empty config file path: %v", configFilePath)
    70  	}
    71  	var config Config
    72  	err = json.Unmarshal(body, &config)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("unable to parse config file: %v", body)
    75  	}
    76  	return &config, nil
    77  }
    78  
    79  func (c *Config) newManifestProperties(deploymentName string) ManifestProperties {
    80  	log.Println("BeforeSuite: releaseVersion =", releaseVersion)
    81  	log.Println("BeforeSuite: winUtilRelVersion =", winUtilRelVersion)
    82  	return ManifestProperties{
    83  		DeploymentName:  deploymentName,
    84  		ReleaseName:     "wuts-release",
    85  		AZ:              c.Az,
    86  		VmType:          c.VmType,
    87  		VmExtensions:    c.VmExtensions,
    88  		Network:         c.Network,
    89  		StemcellOS:      c.StemcellOS,
    90  		StemcellVersion: stemcellInfo.Version,
    91  		WinUtilVersion:  winUtilRelVersion,
    92  		WutsVersion:     releaseVersion,
    93  	}
    94  }
    95  
    96  func (*Config) generateManifestFile(manifestProperties interface{}, manifestTemplate string) (string, error) {
    97  	templ, err := template.New("").Parse(manifestTemplate)
    98  	if err != nil {
    99  		return "", err
   100  	}
   101  
   102  	var buf bytes.Buffer
   103  	err = templ.Execute(&buf, manifestProperties)
   104  	if err != nil {
   105  		return "", err
   106  	}
   107  
   108  	manifestFile, err := ioutil.TempFile("", "")
   109  	if err != nil {
   110  		return "", err
   111  	}
   112  
   113  	manifest := buf.Bytes()
   114  	log.Print("\nManifest: " + string(manifest[:]) + "\n")
   115  	_, err = manifestFile.Write(manifest)
   116  	if err != nil {
   117  		return "", err
   118  	}
   119  
   120  	return filepath.Abs(manifestFile.Name())
   121  }
   122  
   123  func (c *Config) generateDefaultManifest(deploymentName string) (string, error) {
   124  	return c.generateManifestFile(c.newManifestProperties(deploymentName), ManifestTemplate)
   125  }
   126  
   127  type SSHManifestProperties struct {
   128  	ManifestProperties
   129  	SSHEnabled bool
   130  }
   131  
   132  func (c *Config) generateManifestSSH(deploymentName string, enabled bool) (string, error) {
   133  	manifestProperties := SSHManifestProperties{
   134  		ManifestProperties: c.newManifestProperties(deploymentName),
   135  		SSHEnabled:         enabled,
   136  	}
   137  	return c.generateManifestFile(manifestProperties, SSHTemplate)
   138  }
   139  
   140  type RDPManifestProperties struct {
   141  	ManifestProperties
   142  	RDPEnabled         bool
   143  	SetPasswordEnabled bool
   144  	InstanceName       string
   145  	Username           string
   146  	Password           string
   147  }
   148  
   149  func (c *Config) generateManifestRDP(deploymentName string, instanceName string, enabled bool, username string, password string) (string, error) {
   150  	manifestProperties := RDPManifestProperties{
   151  		ManifestProperties: c.newManifestProperties(deploymentName),
   152  		RDPEnabled:         enabled,
   153  		SetPasswordEnabled: enabled,
   154  		InstanceName:       instanceName,
   155  		Username:           username,
   156  		Password:           password,
   157  	}
   158  
   159  	return c.generateManifestFile(manifestProperties, RDPTemplate)
   160  }
   161  
   162  type DefenderManifestProperties struct {
   163  	ManifestProperties
   164  	DefenderEnabled bool
   165  }
   166  
   167  func (c *Config) generateManifestWindowsDefender(deploymentName string, enabled bool) (string, error) {
   168  	manifestProperties := DefenderManifestProperties{
   169  		ManifestProperties: c.newManifestProperties(deploymentName),
   170  		DefenderEnabled:    enabled,
   171  	}
   172  
   173  	return c.generateManifestFile(manifestProperties, DefenderTemplate)
   174  }
   175  
   176  func (c *Config) generateManifestWindowsDefenderChecker(deploymentName string) (string, error) {
   177  	manifestProperties := DefenderManifestProperties{ManifestProperties: c.newManifestProperties(deploymentName)}
   178  
   179  	return c.generateManifestFile(manifestProperties, DefenderNotPresentTemplate)
   180  }
   181  
   182  type BoshCommand struct {
   183  	DirectorIP       string
   184  	Client           string
   185  	ClientSecret     string
   186  	CertPath         string // Path to CA CERT file, if any
   187  	Timeout          time.Duration
   188  	GwPrivateKeyPath string // Path to key file
   189  	GwUser           string
   190  }
   191  
   192  func NewBoshCommand(config *Config, CertPath string, GwPrivateKeyPath string, duration time.Duration) *BoshCommand {
   193  	return &BoshCommand{
   194  		DirectorIP:       config.Bosh.Target,
   195  		Client:           config.Bosh.Client,
   196  		ClientSecret:     config.Bosh.ClientSecret,
   197  		CertPath:         CertPath,
   198  		Timeout:          duration,
   199  		GwPrivateKeyPath: GwPrivateKeyPath,
   200  		GwUser:           config.Bosh.GwUser,
   201  	}
   202  }
   203  
   204  func (c *BoshCommand) args(command string) []string {
   205  	args := strings.Split(command, " ")
   206  	args = append([]string{"-n", "-e", c.DirectorIP, "--client", c.Client, "--client-secret", c.ClientSecret}, args...)
   207  	if c.CertPath != "" {
   208  		args = append([]string{"--ca-cert", c.CertPath}, args...)
   209  	}
   210  	return args
   211  }
   212  
   213  func (c *BoshCommand) Run(command string) error {
   214  	cmd := exec.Command("bosh", c.args(command)...)
   215  	log.Printf("\nRUNNING %q\n", strings.Join(cmd.Args, " "))
   216  
   217  	session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
   218  	if err != nil {
   219  		return err
   220  	}
   221  	session.Wait(c.Timeout)
   222  
   223  	exitCode := session.ExitCode()
   224  	if exitCode != 0 {
   225  		var stderr []byte
   226  		if session.Err != nil {
   227  			stderr = session.Err.Contents()
   228  		}
   229  		stdout := session.Out.Contents()
   230  		return fmt.Errorf("Non-zero exit code for cmd %q: %d\nSTDERR:\n%s\nSTDOUT:%s\n",
   231  			strings.Join(cmd.Args, " "), exitCode, stderr, stdout)
   232  	}
   233  	return nil
   234  }
   235  
   236  func (c *BoshCommand) RunInStdOut(command, dir string) ([]byte, error) {
   237  	cmd := exec.Command("bosh", c.args(command)...)
   238  	if dir != "" {
   239  		cmd.Dir = dir
   240  		log.Printf("\nRUNNING %q IN %q\n", strings.Join(cmd.Args, " "), dir)
   241  	} else {
   242  		log.Printf("\nRUNNING %q\n", strings.Join(cmd.Args, " "))
   243  	}
   244  
   245  	session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	session.Wait(c.Timeout)
   250  
   251  	exitCode := session.ExitCode()
   252  	stdout := session.Out.Contents()
   253  	if exitCode != 0 {
   254  		var stderr []byte
   255  		if session.Err != nil {
   256  			stderr = session.Err.Contents()
   257  		}
   258  		return stdout, fmt.Errorf("Non-zero exit code for cmd %q: %d\nSTDERR:\n%s\nSTDOUT:%s\n",
   259  			strings.Join(cmd.Args, " "), exitCode, stderr, stdout)
   260  	}
   261  	return stdout, nil
   262  }
   263  
   264  func (config *Config) doSSHLogin(targetIP string) *Session {
   265  	sshTunnelAddress := strings.Split(config.Bosh.SSHTunnelIP, ":")[0]
   266  
   267  	session, err := runCommand("ssh",
   268  		"-nNT",
   269  		fmt.Sprintf("%s@%s", bosh.GwUser, sshTunnelAddress),
   270  		"-i",
   271  		bosh.GwPrivateKeyPath,
   272  		"-L",
   273  		fmt.Sprintf("3389:%s:3389", targetIP),
   274  		"-o",
   275  		"StrictHostKeyChecking=no",
   276  		"-o",
   277  		"ExitOnForwardFailure=yes")
   278  	Expect(err).NotTo(HaveOccurred())
   279  
   280  	return session
   281  }
   282  
   283  func runCommand(cmd string, args ...string) (*Session, error) {
   284  	return Start(exec.Command(cmd, args...), GinkgoWriter, GinkgoWriter)
   285  }
   286  
   287  //noinspection GoUnusedFunction
   288  func downloadLogs(deploymentName string, jobName string, index int) *Buffer {
   289  	tempDir, err := ioutil.TempDir("", "")
   290  	Expect(err).To(Succeed())
   291  	defer os.RemoveAll(tempDir)
   292  
   293  	err = bosh.Run(fmt.Sprintf("-d %s logs %s/%d --dir %s", deploymentName, jobName, index, tempDir))
   294  	Expect(err).To(Succeed())
   295  
   296  	matches, err := filepath.Glob(filepath.Join(tempDir, fmt.Sprintf("%s.%s.%d-*.tgz", deploymentName, jobName, index)))
   297  	Expect(err).To(Succeed())
   298  	Expect(matches).To(HaveLen(1))
   299  
   300  	cmd := exec.Command("tar", "xf", matches[0], "-O", fmt.Sprintf("./%s/%s/job-service-wrapper.out.log", jobName, jobName))
   301  	session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
   302  	Expect(err).To(Succeed())
   303  
   304  	return session.Wait().Out
   305  }
   306  
   307  type vmInfo struct {
   308  	Tables []struct {
   309  		Rows []struct {
   310  			Instance string `json:"instance"`
   311  			IPs      string `json:"ips"`
   312  		} `json:"Rows"`
   313  	} `json:"Tables"`
   314  }
   315  
   316  type ManifestInfo struct {
   317  	Version string `yaml:"version"`
   318  	Name    string `yaml:"name"`
   319  }
   320  
   321  func fetchManifestInfo(releasePath string, manifestFilename string) (ManifestInfo, error) {
   322  	var stemcellInfo ManifestInfo
   323  	tempDir, err := ioutil.TempDir("", "")
   324  	Expect(err).To(Succeed())
   325  	defer os.RemoveAll(tempDir)
   326  
   327  	cmd := exec.Command("tar", "xf", releasePath, "-C", tempDir, manifestFilename)
   328  	session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
   329  	Expect(err).To(Succeed())
   330  	session.Wait(20 * time.Minute)
   331  
   332  	exitCode := session.ExitCode()
   333  	if exitCode != 0 {
   334  		var stderr []byte
   335  		if session.Err != nil {
   336  			stderr = session.Err.Contents()
   337  		}
   338  		stdout := session.Out.Contents()
   339  		return stemcellInfo, fmt.Errorf("Non-zero exit code for cmd %q: %d\nSTDERR:\n%s\nSTDOUT:%s\n",
   340  			strings.Join(cmd.Args, " "), exitCode, stderr, stdout)
   341  	}
   342  
   343  	stemcellMF, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", tempDir, manifestFilename))
   344  	Expect(err).To(Succeed())
   345  
   346  	err = yaml.Unmarshal(stemcellMF, &stemcellInfo)
   347  	Expect(err).To(Succeed())
   348  	Expect(stemcellInfo.Version).ToNot(BeNil())
   349  	Expect(stemcellInfo.Version).ToNot(BeEmpty())
   350  
   351  	return stemcellInfo, nil
   352  }
   353  
   354  func writeCert(cert string) string {
   355  	if cert != "" {
   356  		certFile, err := ioutil.TempFile("", "")
   357  		Expect(err).To(Succeed())
   358  
   359  		_, err = certFile.Write([]byte(cert))
   360  		Expect(err).To(Succeed())
   361  
   362  		boshCertPath, err := filepath.Abs(certFile.Name())
   363  		Expect(err).To(Succeed())
   364  		return boshCertPath
   365  	}
   366  	return ""
   367  }
   368  
   369  func createAndUploadRelease(releaseDir string) string {
   370  	pwd, err := os.Getwd()
   371  	Expect(err).To(Succeed())
   372  
   373  	absoluteFilePath := releaseDir
   374  	if !filepath.IsAbs(absoluteFilePath) {
   375  		absoluteFilePath = filepath.Join(pwd, releaseDir)
   376  	}
   377  	Expect(os.Chdir(absoluteFilePath)).To(Succeed())
   378  	defer os.Chdir(pwd)
   379  
   380  	version := fmt.Sprintf("0.dev+%d", getTimestampInMs())
   381  
   382  	Expect(bosh.Run(fmt.Sprintf("create-release --force --version %s", version))).To(Succeed())
   383  	Expect(bosh.Run("upload-release")).To(Succeed())
   384  
   385  	return version
   386  }
   387  
   388  func getTimestampInMs() int64 {
   389  	return time.Now().UTC().UnixNano() / int64(time.Millisecond)
   390  }
   391  
   392  func generateSemiRandomWindowsPassword() string {
   393  	var (
   394  		validChars []rune
   395  		password   string
   396  	)
   397  
   398  	for i := '!'; i <= '~'; i++ {
   399  		if i != '\'' && i != '"' && i != '`' && i != '\\' {
   400  			validChars = append(validChars, i)
   401  		}
   402  	}
   403  
   404  	for i := 0; i < 10; i++ {
   405  		randomIndex := rand.Intn(len(validChars))
   406  		password = password + string(validChars[randomIndex])
   407  	}
   408  
   409  	// ensure compliance with Windows password requirements
   410  	password = password + "Ab!"
   411  	return password
   412  }
   413  
   414  func getFirstInstanceIP(deployment string, instanceName string) (string, error) {
   415  	var vms vmInfo
   416  	stdout, err := bosh.RunInStdOut(fmt.Sprintf("vms -d %s --json", deployment), "")
   417  	if err != nil {
   418  		return "", err
   419  	}
   420  
   421  	if err = json.Unmarshal(stdout, &vms); err != nil {
   422  		return "", err
   423  	}
   424  
   425  	for _, row := range vms.Tables[0].Rows {
   426  		if strings.HasPrefix(row.Instance, instanceName) {
   427  			ips := strings.Split(row.IPs, "\n")
   428  			if len(ips) == 0 {
   429  				break
   430  			}
   431  			return ips[0], nil
   432  		}
   433  	}
   434  
   435  	return "", errors.New("No instance IPs found!")
   436  }