mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-26 17:53:59 -04:00
Merge pull request #5991 from evandbrown/google-disable-default-service-account
builder/googlecompute: Optionally disable service account
This commit is contained in:
@@ -26,39 +26,39 @@ type Config struct {
|
||||
AccountFile string `mapstructure:"account_file"`
|
||||
ProjectId string `mapstructure:"project_id"`
|
||||
|
||||
AcceleratorType string `mapstructure:"accelerator_type"`
|
||||
AcceleratorCount int64 `mapstructure:"accelerator_count"`
|
||||
Address string `mapstructure:"address"`
|
||||
DiskName string `mapstructure:"disk_name"`
|
||||
DiskSizeGb int64 `mapstructure:"disk_size"`
|
||||
DiskType string `mapstructure:"disk_type"`
|
||||
ImageName string `mapstructure:"image_name"`
|
||||
ImageDescription string `mapstructure:"image_description"`
|
||||
ImageFamily string `mapstructure:"image_family"`
|
||||
ImageLabels map[string]string `mapstructure:"image_labels"`
|
||||
ImageLicenses []string `mapstructure:"image_licenses"`
|
||||
InstanceName string `mapstructure:"instance_name"`
|
||||
Labels map[string]string `mapstructure:"labels"`
|
||||
MachineType string `mapstructure:"machine_type"`
|
||||
Metadata map[string]string `mapstructure:"metadata"`
|
||||
Network string `mapstructure:"network"`
|
||||
NetworkProjectId string `mapstructure:"network_project_id"`
|
||||
OmitExternalIP bool `mapstructure:"omit_external_ip"`
|
||||
OnHostMaintenance string `mapstructure:"on_host_maintenance"`
|
||||
Preemptible bool `mapstructure:"preemptible"`
|
||||
RawStateTimeout string `mapstructure:"state_timeout"`
|
||||
Region string `mapstructure:"region"`
|
||||
Scopes []string `mapstructure:"scopes"`
|
||||
SourceImage string `mapstructure:"source_image"`
|
||||
SourceImageFamily string `mapstructure:"source_image_family"`
|
||||
SourceImageProjectId string `mapstructure:"source_image_project_id"`
|
||||
StartupScriptFile string `mapstructure:"startup_script_file"`
|
||||
Subnetwork string `mapstructure:"subnetwork"`
|
||||
Tags []string `mapstructure:"tags"`
|
||||
UseInternalIP bool `mapstructure:"use_internal_ip"`
|
||||
Zone string `mapstructure:"zone"`
|
||||
|
||||
ServiceAccountEmail string `mapstructure:"service_account_email"`
|
||||
AcceleratorType string `mapstructure:"accelerator_type"`
|
||||
AcceleratorCount int64 `mapstructure:"accelerator_count"`
|
||||
Address string `mapstructure:"address"`
|
||||
DisableDefaultServiceAccount bool `mapstructure:"disable_default_service_account"`
|
||||
DiskName string `mapstructure:"disk_name"`
|
||||
DiskSizeGb int64 `mapstructure:"disk_size"`
|
||||
DiskType string `mapstructure:"disk_type"`
|
||||
ImageName string `mapstructure:"image_name"`
|
||||
ImageDescription string `mapstructure:"image_description"`
|
||||
ImageFamily string `mapstructure:"image_family"`
|
||||
ImageLabels map[string]string `mapstructure:"image_labels"`
|
||||
ImageLicenses []string `mapstructure:"image_licenses"`
|
||||
InstanceName string `mapstructure:"instance_name"`
|
||||
Labels map[string]string `mapstructure:"labels"`
|
||||
MachineType string `mapstructure:"machine_type"`
|
||||
Metadata map[string]string `mapstructure:"metadata"`
|
||||
Network string `mapstructure:"network"`
|
||||
NetworkProjectId string `mapstructure:"network_project_id"`
|
||||
OmitExternalIP bool `mapstructure:"omit_external_ip"`
|
||||
OnHostMaintenance string `mapstructure:"on_host_maintenance"`
|
||||
Preemptible bool `mapstructure:"preemptible"`
|
||||
RawStateTimeout string `mapstructure:"state_timeout"`
|
||||
Region string `mapstructure:"region"`
|
||||
Scopes []string `mapstructure:"scopes"`
|
||||
ServiceAccountEmail string `mapstructure:"service_account_email"`
|
||||
SourceImage string `mapstructure:"source_image"`
|
||||
SourceImageFamily string `mapstructure:"source_image_family"`
|
||||
SourceImageProjectId string `mapstructure:"source_image_project_id"`
|
||||
StartupScriptFile string `mapstructure:"startup_script_file"`
|
||||
Subnetwork string `mapstructure:"subnetwork"`
|
||||
Tags []string `mapstructure:"tags"`
|
||||
UseInternalIP bool `mapstructure:"use_internal_ip"`
|
||||
Zone string `mapstructure:"zone"`
|
||||
|
||||
Account AccountFile
|
||||
stateTimeout time.Duration
|
||||
@@ -225,6 +225,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||
errs = packer.MultiErrorAppend(fmt.Errorf("'on_host_maintenance' must be set to 'TERMINATE' when 'accelerator_count' is more than 0"))
|
||||
}
|
||||
|
||||
// If DisableDefaultServiceAccount is provided, don't allow a value for ServiceAccountEmail
|
||||
if c.DisableDefaultServiceAccount && c.ServiceAccountEmail != "" {
|
||||
errs = packer.MultiErrorAppend(fmt.Errorf("you may not specify a 'service_account_email' when 'disable_default_service_account' is true"))
|
||||
}
|
||||
|
||||
// Check for any errors.
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, nil, errs
|
||||
|
||||
@@ -170,6 +170,32 @@ func TestConfigPrepare(t *testing.T) {
|
||||
[]string{"https://www.googleapis.com/auth/cloud-platform"},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"disable_default_service_account",
|
||||
"",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"disable_default_service_account",
|
||||
"NOT A BOOL",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -250,6 +276,55 @@ func TestConfigPrepareAccelerator(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigPrepareServiceAccount(t *testing.T) {
|
||||
cases := []struct {
|
||||
Keys []string
|
||||
Values []interface{}
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
[]string{"disable_default_service_account", "service_account_email"},
|
||||
[]interface{}{true, "[email protected]"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"disable_default_service_account", "service_account_email"},
|
||||
[]interface{}{false, "[email protected]"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"disable_default_service_account", "service_account_email"},
|
||||
[]interface{}{true, ""},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
raw := testConfig(t)
|
||||
|
||||
errStr := ""
|
||||
for k := range tc.Keys {
|
||||
|
||||
// Create the string for error reporting
|
||||
// convert value to string if it can be converted
|
||||
errStr += fmt.Sprintf("%s:%v, ", tc.Keys[k], tc.Values[k])
|
||||
if tc.Values[k] == nil {
|
||||
delete(raw, tc.Keys[k])
|
||||
} else {
|
||||
raw[tc.Keys[k]] = tc.Values[k]
|
||||
}
|
||||
}
|
||||
|
||||
_, warns, errs := NewConfig(raw)
|
||||
|
||||
if tc.Err {
|
||||
testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", "))
|
||||
} else {
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDefaults(t *testing.T) {
|
||||
cases := []struct {
|
||||
Read func(c *Config) interface{}
|
||||
|
||||
@@ -58,28 +58,29 @@ type Driver interface {
|
||||
}
|
||||
|
||||
type InstanceConfig struct {
|
||||
AcceleratorType string
|
||||
AcceleratorCount int64
|
||||
Address string
|
||||
Description string
|
||||
DiskSizeGb int64
|
||||
DiskType string
|
||||
Image *Image
|
||||
Labels map[string]string
|
||||
MachineType string
|
||||
Metadata map[string]string
|
||||
Name string
|
||||
Network string
|
||||
NetworkProjectId string
|
||||
OmitExternalIP bool
|
||||
OnHostMaintenance string
|
||||
Preemptible bool
|
||||
Region string
|
||||
ServiceAccountEmail string
|
||||
Scopes []string
|
||||
Subnetwork string
|
||||
Tags []string
|
||||
Zone string
|
||||
AcceleratorType string
|
||||
AcceleratorCount int64
|
||||
Address string
|
||||
Description string
|
||||
DisableDefaultServiceAccount bool
|
||||
DiskSizeGb int64
|
||||
DiskType string
|
||||
Image *Image
|
||||
Labels map[string]string
|
||||
MachineType string
|
||||
Metadata map[string]string
|
||||
Name string
|
||||
Network string
|
||||
NetworkProjectId string
|
||||
OmitExternalIP bool
|
||||
OnHostMaintenance string
|
||||
Preemptible bool
|
||||
Region string
|
||||
ServiceAccountEmail string
|
||||
Scopes []string
|
||||
Subnetwork string
|
||||
Tags []string
|
||||
Zone string
|
||||
}
|
||||
|
||||
// WindowsPasswordConfig is the data structue that GCE needs to encrypt the created
|
||||
|
||||
@@ -343,12 +343,18 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||
guestAccelerators = append(guestAccelerators, ac)
|
||||
}
|
||||
|
||||
serviceAccount := &compute.ServiceAccount{
|
||||
Email: "default",
|
||||
Scopes: c.Scopes,
|
||||
// Configure the instance's service account. If the user has set
|
||||
// disable_default_service_account, then the default service account
|
||||
// will not be used. If they also do not set service_account_email, then
|
||||
// the instance will be created with no service account or scopes.
|
||||
serviceAccount := &compute.ServiceAccount{}
|
||||
if !c.DisableDefaultServiceAccount {
|
||||
serviceAccount.Email = "default"
|
||||
serviceAccount.Scopes = c.Scopes
|
||||
}
|
||||
if c.ServiceAccountEmail != "" {
|
||||
serviceAccount.Email = c.ServiceAccountEmail
|
||||
serviceAccount.Scopes = c.Scopes
|
||||
}
|
||||
|
||||
// Create the instance information
|
||||
|
||||
@@ -100,28 +100,29 @@ func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
|
||||
var metadata map[string]string
|
||||
metadata, err = c.createInstanceMetadata(sourceImage, sshPublicKey)
|
||||
errCh, err = d.RunInstance(&InstanceConfig{
|
||||
AcceleratorType: c.AcceleratorType,
|
||||
AcceleratorCount: c.AcceleratorCount,
|
||||
Address: c.Address,
|
||||
Description: "New instance created by Packer",
|
||||
DiskSizeGb: c.DiskSizeGb,
|
||||
DiskType: c.DiskType,
|
||||
Image: sourceImage,
|
||||
Labels: c.Labels,
|
||||
MachineType: c.MachineType,
|
||||
Metadata: metadata,
|
||||
Name: name,
|
||||
Network: c.Network,
|
||||
NetworkProjectId: c.NetworkProjectId,
|
||||
OmitExternalIP: c.OmitExternalIP,
|
||||
OnHostMaintenance: c.OnHostMaintenance,
|
||||
Preemptible: c.Preemptible,
|
||||
Region: c.Region,
|
||||
ServiceAccountEmail: c.ServiceAccountEmail,
|
||||
Scopes: c.Scopes,
|
||||
Subnetwork: c.Subnetwork,
|
||||
Tags: c.Tags,
|
||||
Zone: c.Zone,
|
||||
AcceleratorType: c.AcceleratorType,
|
||||
AcceleratorCount: c.AcceleratorCount,
|
||||
Address: c.Address,
|
||||
Description: "New instance created by Packer",
|
||||
DisableDefaultServiceAccount: c.DisableDefaultServiceAccount,
|
||||
DiskSizeGb: c.DiskSizeGb,
|
||||
DiskType: c.DiskType,
|
||||
Image: sourceImage,
|
||||
Labels: c.Labels,
|
||||
MachineType: c.MachineType,
|
||||
Metadata: metadata,
|
||||
Name: name,
|
||||
Network: c.Network,
|
||||
NetworkProjectId: c.NetworkProjectId,
|
||||
OmitExternalIP: c.OmitExternalIP,
|
||||
OnHostMaintenance: c.OnHostMaintenance,
|
||||
Preemptible: c.Preemptible,
|
||||
Region: c.Region,
|
||||
ServiceAccountEmail: c.ServiceAccountEmail,
|
||||
Scopes: c.Scopes,
|
||||
Subnetwork: c.Subnetwork,
|
||||
Tags: c.Tags,
|
||||
Zone: c.Zone,
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -248,6 +248,54 @@ func TestStepCreateInstance_errorTimeout(t *testing.T) {
|
||||
assert.False(t, ok, "State should not have an instance name.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_noServiceAccount(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
c.DisableDefaultServiceAccount = true
|
||||
c.ServiceAccountEmail = ""
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
assert.Equal(t, d.RunInstanceConfig.DisableDefaultServiceAccount, c.DisableDefaultServiceAccount, "Incorrect value for DisableDefaultServiceAccount passed to driver.")
|
||||
assert.Equal(t, d.RunInstanceConfig.ServiceAccountEmail, c.ServiceAccountEmail, "Incorrect value for ServiceAccountEmail passed to driver.")
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_customServiceAccount(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
state.Put("ssh_public_key", "key")
|
||||
|
||||
c := state.Get("config").(*Config)
|
||||
c.DisableDefaultServiceAccount = true
|
||||
c.ServiceAccountEmail = "custom-service-account"
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
d.GetImageResult = StubImage("test-image", "test-project", []string{}, 100)
|
||||
|
||||
// run the step
|
||||
assert.Equal(t, step.Run(context.Background(), state), multistep.ActionContinue, "Step should have passed and continued.")
|
||||
|
||||
// cleanup
|
||||
step.Cleanup(state)
|
||||
|
||||
// Check args passed to the driver.
|
||||
assert.Equal(t, d.RunInstanceConfig.DisableDefaultServiceAccount, c.DisableDefaultServiceAccount, "Incorrect value for DisableDefaultServiceAccount passed to driver.")
|
||||
assert.Equal(t, d.RunInstanceConfig.ServiceAccountEmail, c.ServiceAccountEmail, "Incorrect value for ServiceAccountEmail passed to driver.")
|
||||
}
|
||||
|
||||
func TestCreateInstanceMetadata(t *testing.T) {
|
||||
state := testState(t)
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
@@ -210,6 +210,9 @@ builder.
|
||||
- `address` (string) - The name of a pre-allocated static external IP address.
|
||||
Note, must be the name and not the actual IP address.
|
||||
|
||||
- `disable_default_service_account` (bool) - If true, the default service account will not be used if `service_account_email`
|
||||
is not specified. Set this value to true and omit `service_account_email` to provision a VM with no service account.
|
||||
|
||||
- `disk_name` (string) - The name of the disk, if unset the instance name will be
|
||||
used.
|
||||
|
||||
@@ -269,7 +272,7 @@ builder.
|
||||
to the region hosting the specified `zone`.
|
||||
|
||||
- `service_account_email` (string) - The service account to be used for launched instance. Defaults to
|
||||
the project's default service account.
|
||||
the project's default service account unless `disable_default_service_account` is true.
|
||||
|
||||
- `scopes` (array of strings) - The service account scopes for launched instance.
|
||||
Defaults to:
|
||||
|
||||
Reference in New Issue
Block a user