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 }