Fix perma-diff on instance templates.

When using instance templates, if you use one of our image shorthands at
the moment, you'll get a perma-diff. This is because the config gets
resolved to another format before it is set in state, and so we need to
set that other format in state.

Unfortunately, resolving images requires network access, so we have to
do this with CustomizeDiff. CustomizeDiff was having trouble (I think?
More on this below) on setting a field as not ForceNew once the field
was already set, so I moved the decision for whether a field was
ForceNew or not into CustomizeDiff. I also resolved the old and new
images, and if they were the same, cleared the diff.

Unfortunately, you can't actually clear a field on a sub-block right
now. You have to clear top-level fields only. So this will currently
throw an error. I opened hashicorp/terraform#18795 to fix that. Once
that's merged, and we vendor it here, this patch fixes the problem.

If hashicorp/terraform#18795 doesn't get merged, the next best
workaround is to keep track of _all_ the fields under `disk` with a diff
in our CustomizeDiff, check whether they've all changed or not, and if
they've all changed, clear the changes on `disk`, which I _think_ will
resolve the issue. That's just a massive pain, unfortunately.
This commit is contained in:
Paddy Carver 2018-09-05 17:35:44 -07:00
parent 7911cab2e4
commit 5cf6a5d131
2 changed files with 60 additions and 4 deletions

View File

@ -20,6 +20,7 @@ func resourceComputeInstanceTemplate() *schema.Resource {
State: schema.ImportStatePassthrough,
},
SchemaVersion: 1,
CustomizeDiff: resourceComputeInstanceTemplateCustomizeDiff,
MigrateState: resourceComputeInstanceTemplateMigrateState,
// A compute instance template is more or less a subset of a compute
@ -99,10 +100,9 @@ func resourceComputeInstanceTemplate() *schema.Resource {
},
"source_image": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: compareSelfLinkRelativePaths,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"interface": &schema.Schema{
@ -420,6 +420,48 @@ func resourceComputeInstanceTemplate() *schema.Resource {
}
}
func resourceComputeInstanceTemplateCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error {
config := meta.(*Config)
project, err := getProjectFromDiff(diff, config)
if err != nil {
return err
}
numDisks := diff.Get("disk.#").(int)
for i := 0; i < numDisks; i++ {
key := fmt.Sprintf("disk.%d.source_image", i)
if diff.HasChange(key) {
old, new := diff.GetChange(key)
if old == "" || new == "" {
// no sense in resolving empty strings
err = diff.ForceNew(key)
if err != nil {
return err
}
continue
}
oldResolved, err := resolveImage(config, project, old.(string))
if err != nil {
return err
}
newResolved, err := resolveImage(config, project, new.(string))
if err != nil {
return err
}
if oldResolved != newResolved {
err = diff.ForceNew(key)
if err != nil {
return err
}
}
err = diff.Clear(key)
if err != nil {
return err
}
}
}
return nil
}
func buildDisks(d *schema.ResourceData, config *Config) ([]*computeBeta.AttachedDisk, error) {
project, err := getProject(d, config)
if err != nil {

View File

@ -64,6 +64,20 @@ func getProject(d TerraformResourceData, config *Config) (string, error) {
return getProjectFromSchema("project", d, config)
}
// getProjectFromDiff reads the "project" field from the given diff and falls
// back to the provider's value if not given. If the provider's value is not
// given, an error is returned.
func getProjectFromDiff(d *schema.ResourceDiff, config *Config) (string, error) {
res, ok := d.GetOk("project")
if ok {
return res.(string), nil
}
if config.Project != "" {
return config.Project, nil
}
return "", fmt.Errorf("%s: required field is not set", "project")
}
func getProjectFromInstanceState(is *terraform.InstanceState, config *Config) (string, error) {
res, ok := is.Attributes["project"]