github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/provisioner/chef-client/provisioner.go (about) 1 // This package implements a provisioner for Packer that uses 2 // Chef to provision the remote machine, specifically with chef-client (that is, 3 // with a Chef server). 4 package chefclient 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strings" 14 15 "github.com/hashicorp/packer/common" 16 "github.com/hashicorp/packer/common/uuid" 17 "github.com/hashicorp/packer/helper/config" 18 "github.com/hashicorp/packer/packer" 19 "github.com/hashicorp/packer/provisioner" 20 "github.com/hashicorp/packer/template/interpolate" 21 ) 22 23 type guestOSTypeConfig struct { 24 executeCommand string 25 installCommand string 26 knifeCommand string 27 stagingDir string 28 } 29 30 var guestOSTypeConfigs = map[string]guestOSTypeConfig{ 31 provisioner.UnixOSType: { 32 executeCommand: "{{if .Sudo}}sudo {{end}}chef-client --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", 33 installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash", 34 knifeCommand: "{{if .Sudo}}sudo {{end}}knife {{.Args}} {{.Flags}}", 35 stagingDir: "/tmp/packer-chef-client", 36 }, 37 provisioner.WindowsOSType: { 38 executeCommand: "c:/opscode/chef/bin/chef-client.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}", 39 installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install\"", 40 knifeCommand: "c:/opscode/chef/bin/knife.bat {{.Args}} {{.Flags}}", 41 stagingDir: "C:/Windows/Temp/packer-chef-client", 42 }, 43 } 44 45 type Config struct { 46 common.PackerConfig `mapstructure:",squash"` 47 48 Json map[string]interface{} 49 50 ChefEnvironment string `mapstructure:"chef_environment"` 51 ClientKey string `mapstructure:"client_key"` 52 ConfigTemplate string `mapstructure:"config_template"` 53 EncryptedDataBagSecretPath string `mapstructure:"encrypted_data_bag_secret_path"` 54 ExecuteCommand string `mapstructure:"execute_command"` 55 GuestOSType string `mapstructure:"guest_os_type"` 56 InstallCommand string `mapstructure:"install_command"` 57 KnifeCommand string `mapstructure:"knife_command"` 58 NodeName string `mapstructure:"node_name"` 59 PolicyGroup string `mapstructure:"policy_group"` 60 PolicyName string `mapstructure:"policy_name"` 61 PreventSudo bool `mapstructure:"prevent_sudo"` 62 RunList []string `mapstructure:"run_list"` 63 ServerUrl string `mapstructure:"server_url"` 64 SkipCleanClient bool `mapstructure:"skip_clean_client"` 65 SkipCleanNode bool `mapstructure:"skip_clean_node"` 66 SkipCleanStagingDirectory bool `mapstructure:"skip_clean_staging_directory"` 67 SkipInstall bool `mapstructure:"skip_install"` 68 SslVerifyMode string `mapstructure:"ssl_verify_mode"` 69 TrustedCertsDir string `mapstructure:"trusted_certs_dir"` 70 StagingDir string `mapstructure:"staging_directory"` 71 ValidationClientName string `mapstructure:"validation_client_name"` 72 ValidationKeyPath string `mapstructure:"validation_key_path"` 73 74 ctx interpolate.Context 75 } 76 77 type Provisioner struct { 78 config Config 79 guestOSTypeConfig guestOSTypeConfig 80 guestCommands *provisioner.GuestCommands 81 } 82 83 type ConfigTemplate struct { 84 ChefEnvironment string 85 ClientKey string 86 EncryptedDataBagSecretPath string 87 NodeName string 88 PolicyGroup string 89 PolicyName string 90 ServerUrl string 91 SslVerifyMode string 92 TrustedCertsDir string 93 ValidationClientName string 94 ValidationKeyPath string 95 } 96 97 type ExecuteTemplate struct { 98 ConfigPath string 99 JsonPath string 100 Sudo bool 101 } 102 103 type InstallChefTemplate struct { 104 Sudo bool 105 } 106 107 type KnifeTemplate struct { 108 Sudo bool 109 Flags string 110 Args string 111 } 112 113 func (p *Provisioner) Prepare(raws ...interface{}) error { 114 err := config.Decode(&p.config, &config.DecodeOpts{ 115 Interpolate: true, 116 InterpolateContext: &p.config.ctx, 117 InterpolateFilter: &interpolate.RenderFilter{ 118 Exclude: []string{ 119 "execute_command", 120 "install_command", 121 "knife_command", 122 }, 123 }, 124 }, raws...) 125 if err != nil { 126 return err 127 } 128 129 if p.config.GuestOSType == "" { 130 p.config.GuestOSType = provisioner.DefaultOSType 131 } 132 p.config.GuestOSType = strings.ToLower(p.config.GuestOSType) 133 134 var ok bool 135 p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType] 136 if !ok { 137 return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType) 138 } 139 140 p.guestCommands, err = provisioner.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo) 141 if err != nil { 142 return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType) 143 } 144 145 if p.config.ExecuteCommand == "" { 146 p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand 147 } 148 149 if p.config.InstallCommand == "" { 150 p.config.InstallCommand = p.guestOSTypeConfig.installCommand 151 } 152 153 if p.config.RunList == nil { 154 p.config.RunList = make([]string, 0) 155 } 156 157 if p.config.StagingDir == "" { 158 p.config.StagingDir = p.guestOSTypeConfig.stagingDir 159 } 160 161 if p.config.KnifeCommand == "" { 162 p.config.KnifeCommand = p.guestOSTypeConfig.knifeCommand 163 } 164 165 var errs *packer.MultiError 166 if p.config.ConfigTemplate != "" { 167 fi, err := os.Stat(p.config.ConfigTemplate) 168 if err != nil { 169 errs = packer.MultiErrorAppend( 170 errs, fmt.Errorf("Bad config template path: %s", err)) 171 } else if fi.IsDir() { 172 errs = packer.MultiErrorAppend( 173 errs, fmt.Errorf("Config template path must be a file: %s", err)) 174 } 175 } 176 177 if p.config.EncryptedDataBagSecretPath != "" { 178 pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath) 179 180 if err != nil || pFileInfo.IsDir() { 181 errs = packer.MultiErrorAppend( 182 errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err)) 183 } 184 } 185 186 if p.config.ServerUrl == "" { 187 errs = packer.MultiErrorAppend( 188 errs, fmt.Errorf("server_url must be set")) 189 } 190 191 if p.config.EncryptedDataBagSecretPath != "" { 192 pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath) 193 194 if err != nil || pFileInfo.IsDir() { 195 errs = packer.MultiErrorAppend( 196 errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err)) 197 } 198 } 199 200 if (p.config.PolicyName != "") != (p.config.PolicyGroup != "") { 201 errs = packer.MultiErrorAppend(errs, fmt.Errorf("If either policy_name or policy_group are set, they must both be set.")) 202 } 203 204 jsonValid := true 205 for k, v := range p.config.Json { 206 p.config.Json[k], err = p.deepJsonFix(k, v) 207 if err != nil { 208 errs = packer.MultiErrorAppend( 209 errs, fmt.Errorf("Error processing JSON: %s", err)) 210 jsonValid = false 211 } 212 } 213 214 if jsonValid { 215 // Process the user variables within the JSON and set the JSON. 216 // Do this early so that we can validate and show errors. 217 p.config.Json, err = p.processJsonUserVars() 218 if err != nil { 219 errs = packer.MultiErrorAppend( 220 errs, fmt.Errorf("Error processing user variables in JSON: %s", err)) 221 } 222 } 223 224 if errs != nil && len(errs.Errors) > 0 { 225 return errs 226 } 227 228 return nil 229 } 230 231 func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { 232 233 nodeName := p.config.NodeName 234 if nodeName == "" { 235 nodeName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) 236 } 237 remoteValidationKeyPath := "" 238 serverUrl := p.config.ServerUrl 239 240 if !p.config.SkipInstall { 241 if err := p.installChef(ui, comm); err != nil { 242 return fmt.Errorf("Error installing Chef: %s", err) 243 } 244 } 245 246 if err := p.createDir(ui, comm, p.config.StagingDir); err != nil { 247 return fmt.Errorf("Error creating staging directory: %s", err) 248 } 249 250 if p.config.ClientKey == "" { 251 p.config.ClientKey = fmt.Sprintf("%s/client.pem", p.config.StagingDir) 252 } 253 254 encryptedDataBagSecretPath := "" 255 if p.config.EncryptedDataBagSecretPath != "" { 256 encryptedDataBagSecretPath = fmt.Sprintf("%s/encrypted_data_bag_secret", p.config.StagingDir) 257 if err := p.uploadFile(ui, 258 comm, 259 encryptedDataBagSecretPath, 260 p.config.EncryptedDataBagSecretPath); err != nil { 261 return fmt.Errorf("Error uploading encrypted data bag secret: %s", err) 262 } 263 } 264 265 if p.config.ValidationKeyPath != "" { 266 remoteValidationKeyPath = fmt.Sprintf("%s/validation.pem", p.config.StagingDir) 267 if err := p.uploadFile(ui, comm, remoteValidationKeyPath, p.config.ValidationKeyPath); err != nil { 268 return fmt.Errorf("Error copying validation key: %s", err) 269 } 270 } 271 272 configPath, err := p.createConfig( 273 ui, 274 comm, 275 nodeName, 276 serverUrl, 277 p.config.ClientKey, 278 encryptedDataBagSecretPath, 279 remoteValidationKeyPath, 280 p.config.ValidationClientName, 281 p.config.ChefEnvironment, 282 p.config.PolicyGroup, 283 p.config.PolicyName, 284 p.config.SslVerifyMode, 285 p.config.TrustedCertsDir) 286 if err != nil { 287 return fmt.Errorf("Error creating Chef config file: %s", err) 288 } 289 290 jsonPath, err := p.createJson(ui, comm) 291 if err != nil { 292 return fmt.Errorf("Error creating JSON attributes: %s", err) 293 } 294 295 err = p.executeChef(ui, comm, configPath, jsonPath) 296 297 if !(p.config.SkipCleanNode && p.config.SkipCleanClient) { 298 299 knifeConfigPath, knifeErr := p.createKnifeConfig( 300 ui, comm, nodeName, serverUrl, p.config.ClientKey, p.config.SslVerifyMode, p.config.TrustedCertsDir) 301 302 if knifeErr != nil { 303 return fmt.Errorf("Error creating knife config on node: %s", knifeErr) 304 } 305 306 if !p.config.SkipCleanNode { 307 if err := p.cleanNode(ui, comm, nodeName, knifeConfigPath); err != nil { 308 return fmt.Errorf("Error cleaning up chef node: %s", err) 309 } 310 } 311 312 if !p.config.SkipCleanClient { 313 if err := p.cleanClient(ui, comm, nodeName, knifeConfigPath); err != nil { 314 return fmt.Errorf("Error cleaning up chef client: %s", err) 315 } 316 } 317 } 318 319 if err != nil { 320 return fmt.Errorf("Error executing Chef: %s", err) 321 } 322 323 if !p.config.SkipCleanStagingDirectory { 324 if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil { 325 return fmt.Errorf("Error removing %s: %s", p.config.StagingDir, err) 326 } 327 } 328 329 return nil 330 } 331 332 func (p *Provisioner) Cancel() { 333 // Just hard quit. It isn't a big deal if what we're doing keeps 334 // running on the other side. 335 os.Exit(0) 336 } 337 338 func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, remotePath string, localPath string) error { 339 ui.Message(fmt.Sprintf("Uploading %s...", localPath)) 340 341 f, err := os.Open(localPath) 342 if err != nil { 343 return err 344 } 345 defer f.Close() 346 347 return comm.Upload(remotePath, f, nil) 348 } 349 350 func (p *Provisioner) createConfig( 351 ui packer.Ui, 352 comm packer.Communicator, 353 nodeName string, 354 serverUrl string, 355 clientKey string, 356 encryptedDataBagSecretPath, 357 remoteKeyPath string, 358 validationClientName string, 359 chefEnvironment string, 360 policyGroup string, 361 policyName string, 362 sslVerifyMode string, 363 trustedCertsDir string) (string, error) { 364 365 ui.Message("Creating configuration file 'client.rb'") 366 367 // Read the template 368 tpl := DefaultConfigTemplate 369 if p.config.ConfigTemplate != "" { 370 f, err := os.Open(p.config.ConfigTemplate) 371 if err != nil { 372 return "", err 373 } 374 defer f.Close() 375 376 tplBytes, err := ioutil.ReadAll(f) 377 if err != nil { 378 return "", err 379 } 380 381 tpl = string(tplBytes) 382 } 383 384 ctx := p.config.ctx 385 ctx.Data = &ConfigTemplate{ 386 NodeName: nodeName, 387 ServerUrl: serverUrl, 388 ClientKey: clientKey, 389 ValidationKeyPath: remoteKeyPath, 390 ValidationClientName: validationClientName, 391 ChefEnvironment: chefEnvironment, 392 PolicyGroup: policyGroup, 393 PolicyName: policyName, 394 SslVerifyMode: sslVerifyMode, 395 TrustedCertsDir: trustedCertsDir, 396 EncryptedDataBagSecretPath: encryptedDataBagSecretPath, 397 } 398 configString, err := interpolate.Render(tpl, &ctx) 399 if err != nil { 400 return "", err 401 } 402 403 remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "client.rb")) 404 if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { 405 return "", err 406 } 407 408 return remotePath, nil 409 } 410 411 func (p *Provisioner) createKnifeConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, sslVerifyMode string, trustedCertsDir string) (string, error) { 412 ui.Message("Creating configuration file 'knife.rb'") 413 414 // Read the template 415 tpl := DefaultKnifeTemplate 416 417 ctx := p.config.ctx 418 ctx.Data = &ConfigTemplate{ 419 NodeName: nodeName, 420 ServerUrl: serverUrl, 421 ClientKey: clientKey, 422 SslVerifyMode: sslVerifyMode, 423 TrustedCertsDir: trustedCertsDir, 424 } 425 configString, err := interpolate.Render(tpl, &ctx) 426 if err != nil { 427 return "", err 428 } 429 430 remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "knife.rb")) 431 if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { 432 return "", err 433 } 434 435 return remotePath, nil 436 } 437 438 func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string, error) { 439 ui.Message("Creating JSON attribute file") 440 441 jsonData := make(map[string]interface{}) 442 443 // Copy the configured JSON 444 for k, v := range p.config.Json { 445 jsonData[k] = v 446 } 447 448 // Set the run list if it was specified 449 if len(p.config.RunList) > 0 { 450 jsonData["run_list"] = p.config.RunList 451 } 452 453 jsonBytes, err := json.MarshalIndent(jsonData, "", " ") 454 if err != nil { 455 return "", err 456 } 457 458 // Upload the bytes 459 remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "first-boot.json")) 460 if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil { 461 return "", err 462 } 463 464 return remotePath, nil 465 } 466 467 func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { 468 ui.Message(fmt.Sprintf("Creating directory: %s", dir)) 469 470 cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)} 471 if err := cmd.StartWithUi(comm, ui); err != nil { 472 return err 473 } 474 if cmd.ExitStatus != 0 { 475 return fmt.Errorf("Non-zero exit status. See output above for more info.") 476 } 477 478 // Chmod the directory to 0777 just so that we can access it as our user 479 cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")} 480 if err := cmd.StartWithUi(comm, ui); err != nil { 481 return err 482 } 483 if cmd.ExitStatus != 0 { 484 return fmt.Errorf("Non-zero exit status. See output above for more info.") 485 } 486 487 return nil 488 } 489 490 func (p *Provisioner) cleanNode(ui packer.Ui, comm packer.Communicator, node string, knifeConfigPath string) error { 491 ui.Say("Cleaning up chef node...") 492 args := []string{"node", "delete", node} 493 if err := p.knifeExec(ui, comm, node, knifeConfigPath, args); err != nil { 494 return fmt.Errorf("Failed to cleanup node: %s", err) 495 } 496 497 return nil 498 } 499 500 func (p *Provisioner) cleanClient(ui packer.Ui, comm packer.Communicator, node string, knifeConfigPath string) error { 501 ui.Say("Cleaning up chef client...") 502 args := []string{"client", "delete", node} 503 if err := p.knifeExec(ui, comm, node, knifeConfigPath, args); err != nil { 504 return fmt.Errorf("Failed to cleanup client: %s", err) 505 } 506 507 return nil 508 } 509 510 func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node string, knifeConfigPath string, args []string) error { 511 flags := []string{ 512 "-y", 513 "-c", knifeConfigPath, 514 } 515 516 p.config.ctx.Data = &KnifeTemplate{ 517 Sudo: !p.config.PreventSudo, 518 Flags: strings.Join(flags, " "), 519 Args: strings.Join(args, " "), 520 } 521 522 command, err := interpolate.Render(p.config.KnifeCommand, &p.config.ctx) 523 if err != nil { 524 return err 525 } 526 527 cmd := &packer.RemoteCmd{Command: command} 528 if err := cmd.StartWithUi(comm, ui); err != nil { 529 return err 530 } 531 if cmd.ExitStatus != 0 { 532 return fmt.Errorf( 533 "Non-zero exit status. See output above for more info.\n\n"+ 534 "Command: %s", 535 command) 536 } 537 538 return nil 539 } 540 541 func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { 542 ui.Message(fmt.Sprintf("Removing directory: %s", dir)) 543 544 cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)} 545 if err := cmd.StartWithUi(comm, ui); err != nil { 546 return err 547 } 548 549 return nil 550 } 551 552 func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config string, json string) error { 553 p.config.ctx.Data = &ExecuteTemplate{ 554 ConfigPath: config, 555 JsonPath: json, 556 Sudo: !p.config.PreventSudo, 557 } 558 command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) 559 if err != nil { 560 return err 561 } 562 563 ui.Message(fmt.Sprintf("Executing Chef: %s", command)) 564 565 cmd := &packer.RemoteCmd{ 566 Command: command, 567 } 568 569 if err := cmd.StartWithUi(comm, ui); err != nil { 570 return err 571 } 572 573 if cmd.ExitStatus != 0 { 574 return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus) 575 } 576 577 return nil 578 } 579 580 func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error { 581 ui.Message("Installing Chef...") 582 583 p.config.ctx.Data = &InstallChefTemplate{ 584 Sudo: !p.config.PreventSudo, 585 } 586 command, err := interpolate.Render(p.config.InstallCommand, &p.config.ctx) 587 if err != nil { 588 return err 589 } 590 591 ui.Message(command) 592 593 cmd := &packer.RemoteCmd{Command: command} 594 if err := cmd.StartWithUi(comm, ui); err != nil { 595 return err 596 } 597 598 if cmd.ExitStatus != 0 { 599 return fmt.Errorf( 600 "Install script exited with non-zero exit status %d", cmd.ExitStatus) 601 } 602 603 return nil 604 } 605 606 func (p *Provisioner) deepJsonFix(key string, current interface{}) (interface{}, error) { 607 if current == nil { 608 return nil, nil 609 } 610 611 switch c := current.(type) { 612 case []interface{}: 613 val := make([]interface{}, len(c)) 614 for i, v := range c { 615 var err error 616 val[i], err = p.deepJsonFix(fmt.Sprintf("%s[%d]", key, i), v) 617 if err != nil { 618 return nil, err 619 } 620 } 621 622 return val, nil 623 case []uint8: 624 return string(c), nil 625 case map[interface{}]interface{}: 626 val := make(map[string]interface{}) 627 for k, v := range c { 628 ks, ok := k.(string) 629 if !ok { 630 return nil, fmt.Errorf("%s: key is not string", key) 631 } 632 633 var err error 634 val[ks], err = p.deepJsonFix( 635 fmt.Sprintf("%s.%s", key, ks), v) 636 if err != nil { 637 return nil, err 638 } 639 } 640 641 return val, nil 642 default: 643 return current, nil 644 } 645 } 646 647 func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) { 648 jsonBytes, err := json.Marshal(p.config.Json) 649 if err != nil { 650 // This really shouldn't happen since we literally just unmarshalled 651 panic(err) 652 } 653 654 // Copy the user variables so that we can restore them later, and 655 // make sure we make the quotes JSON-friendly in the user variables. 656 originalUserVars := make(map[string]string) 657 for k, v := range p.config.ctx.UserVariables { 658 originalUserVars[k] = v 659 } 660 661 // Make sure we reset them no matter what 662 defer func() { 663 p.config.ctx.UserVariables = originalUserVars 664 }() 665 666 // Make the current user variables JSON string safe. 667 for k, v := range p.config.ctx.UserVariables { 668 v = strings.Replace(v, `\`, `\\`, -1) 669 v = strings.Replace(v, `"`, `\"`, -1) 670 p.config.ctx.UserVariables[k] = v 671 } 672 673 // Process the bytes with the template processor 674 p.config.ctx.Data = nil 675 jsonBytesProcessed, err := interpolate.Render(string(jsonBytes), &p.config.ctx) 676 if err != nil { 677 return nil, err 678 } 679 680 var result map[string]interface{} 681 if err := json.Unmarshal([]byte(jsonBytesProcessed), &result); err != nil { 682 return nil, err 683 } 684 685 return result, nil 686 } 687 688 var DefaultConfigTemplate = ` 689 log_level :info 690 log_location STDOUT 691 chef_server_url "{{.ServerUrl}}" 692 client_key "{{.ClientKey}}" 693 {{if ne .EncryptedDataBagSecretPath ""}} 694 encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}" 695 {{end}} 696 {{if ne .ValidationClientName ""}} 697 validation_client_name "{{.ValidationClientName}}" 698 {{else}} 699 validation_client_name "chef-validator" 700 {{end}} 701 {{if ne .ValidationKeyPath ""}} 702 validation_key "{{.ValidationKeyPath}}" 703 {{end}} 704 node_name "{{.NodeName}}" 705 {{if ne .ChefEnvironment ""}} 706 environment "{{.ChefEnvironment}}" 707 {{end}} 708 {{if ne .PolicyGroup ""}} 709 policy_group "{{.PolicyGroup}}" 710 {{end}} 711 {{if ne .PolicyName ""}} 712 policy_name "{{.PolicyName}}" 713 {{end}} 714 {{if ne .SslVerifyMode ""}} 715 ssl_verify_mode :{{.SslVerifyMode}} 716 {{end}} 717 {{if ne .TrustedCertsDir ""}} 718 trusted_certs_dir "{{.TrustedCertsDir}}" 719 {{end}} 720 ` 721 722 var DefaultKnifeTemplate = ` 723 log_level :info 724 log_location STDOUT 725 chef_server_url "{{.ServerUrl}}" 726 client_key "{{.ClientKey}}" 727 node_name "{{.NodeName}}" 728 {{if ne .SslVerifyMode ""}} 729 ssl_verify_mode :{{.SslVerifyMode}} 730 {{end}} 731 {{if ne .TrustedCertsDir ""}} 732 trusted_certs_dir "{{.TrustedCertsDir}}" 733 {{end}} 734 `