Merge pull request #6579 from hashicorp/reinterpolate_winrm_step

use statebag instead of SetSharedState for winRM password
This commit is contained in:
Megan Marsh 2018-08-24 14:02:13 -07:00 committed by GitHub
commit d356817890
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 128 additions and 67 deletions

View File

@ -12,7 +12,6 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -95,15 +94,13 @@ WaitLoop:
"Password (since debug is enabled): %s", s.Comm.WinRMPassword))
}
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName)
state.Put("winrm_password", s.Comm.WinRMPassword)
packer.LogSecretFilter.Set(s.Comm.WinRMPassword)
return multistep.ActionContinue
}
func (s *StepGetPassword) Cleanup(multistep.StateBag) {
commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName)
}
func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) {

View File

@ -22,7 +22,6 @@ import (
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/builder/azure/pkcs12"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
@ -361,10 +360,7 @@ func setRuntimeValues(c *Config) {
var tempName = NewTempName()
c.tmpAdminPassword = tempName.AdminPassword
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", c.tmpAdminPassword, c.PackerConfig.PackerBuildName)
packer.LogSecretFilter.Set(c.tmpAdminPassword)
c.tmpCertificatePassword = tempName.CertificatePassword
if c.TempComputeName == "" {
c.tmpComputeName = tempName.ComputeName

View File

@ -3,7 +3,6 @@ package arm
import (
"context"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -15,11 +14,10 @@ type StepSaveWinRMPassword struct {
func (s *StepSaveWinRMPassword) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Password, s.BuildName)
state.Put("winrm_password", s.Password)
packer.LogSecretFilter.Set(s.Password)
return multistep.ActionContinue
}
func (s *StepSaveWinRMPassword) Cleanup(multistep.StateBag) {
commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName)
}

View File

@ -13,7 +13,6 @@ import (
"os"
"time"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -114,7 +113,6 @@ func (s *StepCreateWindowsPassword) Run(_ context.Context, state multistep.State
}
state.Put("winrm_password", data.password)
commonhelper.SetSharedState("winrm_password", data.password, c.PackerConfig.PackerBuildName)
packer.LogSecretFilter.Set(data.password)
return multistep.ActionContinue

View File

@ -5,7 +5,6 @@ import (
"fmt"
"log"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -52,8 +51,9 @@ func (s *stepGetDefaultCredentials) Run(ctx context.Context, state multistep.Sta
}
// store so that we can access this later during provisioning
commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName)
state.Put("winrm_password", s.Comm.WinRMPassword)
packer.LogSecretFilter.Set(s.Comm.WinRMPassword)
return multistep.ActionContinue
}

View File

@ -53,6 +53,9 @@ type Config struct {
UseLinuxPathing bool `mapstructure:"use_linux_pathing"`
Ctx interpolate.Context
// internal use only; for the provisioner.
WinRMPassword string
}
func Decode(config *Config, raws ...interface{}) error {

View File

@ -10,7 +10,6 @@ import (
"strings"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
@ -70,6 +69,7 @@ func Run(ui packer.Ui, config *Config) (bool, error) {
// buffers and for reading the final exit status.
flattenedCmd := strings.Join(interpolatedCmds, " ")
cmd := &packer.RemoteCmd{Command: flattenedCmd}
packer.LogSecretFilter.Set(config.WinRMPassword)
log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd)
if err := cmd.StartWithUi(comm, ui); err != nil {
return false, fmt.Errorf(
@ -105,7 +105,7 @@ func createInlineScriptFile(config *Config) (string, error) {
// generate context so you can interpolate the command
config.Ctx.Data = &EnvVarsTemplate{
WinRMPassword: getWinRMPassword(config.PackerBuildName),
WinRMPassword: config.WinRMPassword,
}
for _, command := range config.Inline {
@ -139,7 +139,7 @@ func createInterpolatedCommands(config *Config, script string, flattenedEnvVars
Vars: flattenedEnvVars,
Script: script,
Command: script,
WinRMPassword: getWinRMPassword(config.PackerBuildName),
WinRMPassword: config.WinRMPassword,
}
interpolatedCmds := make([]string, len(config.ExecuteCommand))
@ -169,7 +169,7 @@ func createFlattenedEnvVars(config *Config) (string, error) {
// interpolate environment variables
config.Ctx.Data = &EnvVarsTemplate{
WinRMPassword: getWinRMPassword(config.PackerBuildName),
WinRMPassword: config.WinRMPassword,
}
// Split vars into key/value components
for _, envVar := range config.Vars {
@ -196,9 +196,3 @@ func createFlattenedEnvVars(config *Config) (string, error) {
}
return flattened, nil
}
func getWinRMPassword(buildName string) string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName)
packer.LogSecretFilter.Set(winRMPass)
return winRMPass
}

View File

@ -22,6 +22,10 @@ type StepProvision struct {
Comm packer.Communicator
}
type ProvisionHookData struct {
WinRMPassword string
}
func (s *StepProvision) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
comm := s.Comm
if comm == nil {
@ -33,12 +37,21 @@ func (s *StepProvision) Run(_ context.Context, state multistep.StateBag) multist
hook := state.Get("hook").(packer.Hook)
ui := state.Get("ui").(packer.Ui)
// Save data we need to give to provisioners
WinRMPassword, ok := state.GetOk("winrm_password")
if !ok {
WinRMPassword = ""
} else {
WinRMPassword = WinRMPassword.(string)
}
phd := ProvisionHookData{WinRMPassword.(string)}
// Run the provisioner in a goroutine so we can continually check
// for cancellations...
log.Println("Running the provision hook")
errCh := make(chan error, 1)
go func() {
errCh <- hook.Run(packer.HookProvision, ui, comm, nil)
errCh <- hook.Run(packer.HookProvision, ui, comm, phd)
}()
for {

View File

@ -44,6 +44,10 @@ type ProvisionHook struct {
runningProvisioner Provisioner
}
type ProvisionHookData struct {
WinRMPassword string
}
// Runs the provisioners in order.
func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
// Shortcut
@ -71,8 +75,15 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac
h.lock.Unlock()
ts := CheckpointReporter.AddSpan(p.TypeName, "provisioner", p.Config)
// re-run prepare with builder-generated config variables (e.g. WinRMPassword)
// Hack Alert. #SorryNotSorry.
err := p.Provisioner.Prepare(data)
if err != nil {
log.Printf("Error performing secondary Prepare: %s", err)
}
err := p.Provisioner.Provision(ui, comm)
// Finally, provision.
err = p.Provisioner.Provision(ui, comm)
ts.End(err)
if err != nil {

View File

@ -14,8 +14,10 @@ type MockProvisioner struct {
}
func (t *MockProvisioner) Prepare(configs ...interface{}) error {
t.PrepCalled = true
t.PrepConfigs = configs
if !t.PrepCalled {
t.PrepCalled = true
t.PrepConfigs = configs
}
return nil
}

View File

@ -26,7 +26,6 @@ import (
"golang.org/x/crypto/ssh"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -58,6 +57,8 @@ type Config struct {
UseSFTP bool `mapstructure:"use_sftp"`
InventoryDirectory string `mapstructure:"inventory_directory"`
InventoryFile string `mapstructure:"inventory_file"`
// for internal use only; unsettable by user.
winrmpassword string
}
type Provisioner struct {
@ -73,14 +74,35 @@ type PassthroughTemplate struct {
}
func (p *Provisioner) Prepare(raws ...interface{}) error {
// This is a bit of a hack. For provisioners that need access to
// auto-generated WinRMPasswords, the mechanism of keeping provisioner data
// and build data totally segregated breaks down. We get around this by
// having the builder stash the WinRMPassword in the state bag, then
// grabbing it out of the statebag inside of StepProvision. Then, when
// the time comes to provision for real, we run the prepare step one more
// time, now with WinRMPassword defined in the raws, and can store the
// password on the provisioner config without overwriting the rest of the
// work we've already done in the first prepare run.
if len(raws) == 1 {
for k, v := range raws[0].(map[interface{}]interface{}) {
if k.(string) == "WinRMPassword" {
p.config.winrmpassword = v.(string)
// Even if WinRMPassword is not gonna be used, we've stored the
// key and pointed it to an empty string. That means we'll
// always reach this on our second-run of Prepare()
return nil
}
}
}
p.done = make(chan struct{})
// Create passthrough for winrm password so we can fill it in once we know
// it
// don't interpolate winrmpassword yet; if we've made it to this line, then
// we have not obtained the winrm password yet.
p.config.ctx.Data = &PassthroughTemplate{
WinRMPassword: `{{.WinRMPassword}}`,
}
err := config.Decode(&p.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &p.config.ctx,
@ -200,8 +222,9 @@ func (p *Provisioner) getVersion() error {
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Say("Provisioning with Ansible...")
// Interpolate env vars to check for .WinRMPassword
packer.LogSecretFilter.Set(p.config.winrmpassword)
p.config.ctx.Data = &PassthroughTemplate{
WinRMPassword: getWinRMPassword(p.config.PackerBuildName),
WinRMPassword: p.config.winrmpassword,
}
for i, envVar := range p.config.AnsibleEnvVars {
envVar, err := interpolate.Render(envVar, &p.config.ctx)
@ -420,12 +443,7 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator, pri
// remove winrm password from command, if it's been added
flattenedCmd := strings.Join(cmd.Args, " ")
sanitized := flattenedCmd
if len(getWinRMPassword(p.config.PackerBuildName)) > 0 {
sanitized = strings.Replace(sanitized,
getWinRMPassword(p.config.PackerBuildName), "*****", -1)
}
ui.Say(fmt.Sprintf("Executing Ansible: %s", sanitized))
ui.Say(fmt.Sprintf("Executing Ansible: %s", flattenedCmd))
if err := cmd.Start(); err != nil {
return err
@ -553,12 +571,6 @@ func newSigner(privKeyFile string) (*signer, error) {
return signer, nil
}
func getWinRMPassword(buildName string) string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName)
packer.LogSecretFilter.Set(winRMPass)
return winRMPass
}
// Ui provides concurrency-safe access to packer.Ui.
type Ui struct {
sem chan int

View File

@ -16,8 +16,8 @@ import (
// Be sure to remove the Ansible stub file in each test with:
// defer os.Remove(config["command"].(string))
func testConfig(t *testing.T) map[string]interface{} {
m := make(map[string]interface{})
func testConfig(t *testing.T) map[interface{}]interface{} {
m := make((map[interface{}]interface{}))
wd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)

View File

@ -17,7 +17,6 @@ import (
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/uuid"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -98,6 +97,9 @@ type Config struct {
// Changes will not be effective until the system is rebooted."
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
// internal variable, to be written to at provisioner run time.
winrmpassword string
ctx interpolate.Context
}
@ -117,8 +119,28 @@ type EnvVarsTemplate struct {
}
func (p *Provisioner) Prepare(raws ...interface{}) error {
// Create passthrough for winrm password so we can fill it in once we know
// it
// This is a bit of a hack. For provisioners that need access to
// auto-generated WinRMPasswords, the mechanism of keeping provisioner data
// and build data totally segregated breaks down. We get around this by
// having the builder stash the WinRMPassword in the state bag, then
// grabbing it out of the statebag inside of StepProvision. Then, when
// the time comes to provision for real, we run the prepare step one more
// time, now with WinRMPassword defined in the raws, and can store the
// password on the provisioner config without overwriting the rest of the
// work we've already done in the first prepare run.
if len(raws) == 1 {
for k, v := range raws[0].(map[interface{}]interface{}) {
if k.(string) == "WinRMPassword" {
p.config.winrmpassword = v.(string)
// Even if WinRMPassword is not gonna be used, we've stored the
// key and pointed it to an empty string. That means we'll
// always reach this on our second-run of Prepare()
return nil
}
}
}
// Only get here if it's the first time the Provisioner's Prepare is run
p.config.ctx.Data = &EnvVarsTemplate{
WinRMPassword: `{{.WinRMPassword}}`,
}
@ -135,7 +157,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
}, raws...)
if err != nil {
return err
return fmt.Errorf("Error decoding powershell provisioner template: %s", err)
}
if p.config.EnvVarFormat == "" {
@ -261,7 +283,7 @@ func extractScript(p *Provisioner) (string, error) {
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Say(fmt.Sprintf("Provisioning with Powershell..."))
p.communicator = comm
packer.LogSecretFilter.Set(p.config.winrmpassword)
scripts := make([]string, len(p.config.Scripts))
copy(scripts, p.config.Scripts)
@ -392,7 +414,7 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) {
// interpolate environment variables
p.config.ctx.Data = &EnvVarsTemplate{
WinRMPassword: getWinRMPassword(p.config.PackerBuildName),
WinRMPassword: p.config.winrmpassword,
}
// Split vars into key/value components
for _, envVar := range p.config.Vars {
@ -467,7 +489,7 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro
p.config.ctx.Data = &ExecuteCommandTemplate{
Path: p.config.RemotePath,
Vars: p.config.RemoteEnvVarPath,
WinRMPassword: getWinRMPassword(p.config.PackerBuildName),
WinRMPassword: p.config.winrmpassword,
}
command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
@ -479,12 +501,6 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro
return command, nil
}
func getWinRMPassword(buildName string) string {
winRMPass, _ := commonhelper.RetrieveSharedState("winrm_password", buildName)
packer.LogSecretFilter.Set(winRMPass)
return winRMPass
}
func (p *Provisioner) createCommandTextPrivileged() (command string, err error) {
// Prepare everything needed to enable the required env vars within the
// remote environment
@ -496,7 +512,7 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error)
p.config.ctx.Data = &ExecuteCommandTemplate{
Path: p.config.RemotePath,
Vars: p.config.RemoteEnvVarPath,
WinRMPassword: getWinRMPassword(p.config.PackerBuildName),
WinRMPassword: p.config.winrmpassword,
}
command, err = interpolate.Render(p.config.ElevatedExecuteCommand, &p.config.ctx)
if err != nil {
@ -555,7 +571,7 @@ func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath strin
}
// Replace ElevatedPassword for winrm users who used this feature
p.config.ctx.Data = &EnvVarsTemplate{
WinRMPassword: getWinRMPassword(p.config.PackerBuildName),
WinRMPassword: p.config.winrmpassword,
}
p.config.ElevatedPassword, _ = interpolate.Render(p.config.ElevatedPassword, &p.config.ctx)

View File

@ -14,8 +14,8 @@ import (
"github.com/hashicorp/packer/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
func testConfig() map[interface{}]interface{} {
return map[interface{}]interface{}{
"inline": []interface{}{"foo", "bar"},
}
}

View File

@ -10,6 +10,27 @@ type Provisioner struct {
}
func (p *Provisioner) Prepare(raws ...interface{}) error {
// This is a bit of a hack. For provisioners that need access to
// auto-generated WinRMPasswords, the mechanism of keeping provisioner data
// and build data totally segregated breaks down. We get around this by
// having the builder stash the WinRMPassword in the state bag, then
// grabbing it out of the statebag inside of StepProvision. Then, when
// the time comes to provision for real, we run the prepare step one more
// time, now with WinRMPassword defined in the raws, and can store the
// password on the provisioner config without overwriting the rest of the
// work we've already done in the first prepare run.
if len(raws) == 1 {
for k, v := range raws[0].(map[interface{}]interface{}) {
if k.(string) == "WinRMPassword" {
p.config.WinRMPassword = v.(string)
// Even if WinRMPassword is not gonna be used, we've stored the
// key and pointed it to an empty string. That means we'll
// always reach this on our second-run of Prepare()
return nil
}
}
}
err := sl.Decode(&p.config, raws...)
if err != nil {
return err

View File

@ -48,8 +48,8 @@ func TestConfigPrepare(t *testing.T) {
}
}
func testConfig(t *testing.T) map[string]interface{} {
return map[string]interface{}{
func testConfig(t *testing.T) map[interface{}]interface{} {
return map[interface{}]interface{}{
"command": "echo foo",
}
}