vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go
func (r *DiskSubresource) DiffExisting() error {
	log.Printf("[DEBUG] %s: Beginning normalization of existing disk", r)
	name, err := diskLabelOrName(r.data)
	if err != nil {
		return err
	}
	// Prevent a backward migration of label -> name. TODO: Remove this after
	// 2.0.
	olabel, nlabel := r.GetChange("label")
	if olabel != "" && nlabel == "" {
		return errors.New("cannot migrate from label to name")
	}
	// Carry forward the name attribute like we used to if no label is defined.
	// TODO: Remove this after 2.0.
	if nlabel == "" {
		oname, _ := r.GetChange("name")
		r.Set("name", oname.(string))
	}

	// set some computed fields: key, device_address, and uuid will always be
	// non-populated, so copy those.
	okey, _ := r.GetChange("key")
	odaddr, _ := r.GetChange("device_address")
	ouuid, _ := r.GetChange("uuid")
	r.Set("key", okey)
	r.Set("device_address", odaddr)
	r.Set("uuid", ouuid)

	if !r.Get("attach").(bool) {
		// Carry forward path when attach is not set
		opath, _ := r.GetChange("path")
		r.Set("path", opath.(string))
	}

	// Set the datastore if it's missing as we infer this from the default
	// datastore in that case
	if r.Get("datastore_id") == "" {
		switch {
		case r.rdd.HasChange("datastore_id"):
			// If the default datastore is changing and we don't have a default
			// datastore here, we need to use the implicit setting here to indicate
			// that we may need to migrate. This allows us to differentiate between a
			// full storage vMotion no-op, an implicit migration, and a migration
			// where we will need to generate a relocate spec for the individual disk
			// to ensure it stays at a datastore it might be pinned on.
			dsID := r.rdd.Get("datastore_id").(string)
			if dsID == "" {
				r.Set("datastore_id", diskDatastoreComputedName)
			} else {
				r.Set("datastore_id", dsID)
			}
		default:
			if err = r.normalizeDiskDatastore(); err != nil {
				return err
			}
		}
	}

	// Preserve the share value if we don't have custom shares set
	osc, _ := r.GetChange("io_share_count")
	if r.Get("io_share_level").(string) != string(types.SharesLevelCustom) {
		r.Set("io_share_count", osc)
	}

	// Ensure that the user is not attempting to shrink the disk. If we do more
	// we might want to change the name of this method, but we want to check this
	// here as CustomizeDiff is meant for vetoing.
	osize, nsize := r.GetChange("size")
	if osize.(int) > nsize.(int) {
		return fmt.Errorf("virtual disk %q: virtual disks cannot be shrunk (old: %d new: %d)", name, osize.(int), nsize.(int))
	}

	// Ensure that there is no change in either eagerly_scrub or thin_provisioned
	// - these values cannot be changed once set.
	if _, err = r.GetWithVeto("eagerly_scrub"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}
	if _, err = r.GetWithVeto("thin_provisioned"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}
	// Same with attach
	if _, err = r.GetWithVeto("attach"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}

	// Validate storage vMotion if the datastore is changing
	if r.HasChange("datastore_id") {
		if err = r.validateStorageRelocateDiff(); err != nil {
			return err
		}
	}
	log.Printf("[DEBUG] %s: Normalization of existing disk diff complete", r)
	return nil
}
func (r *DiskSubresource) DiffExisting() error {
	log.Printf("[DEBUG] %s: Beginning normalization of existing disk", r)
	name, err := diskLabelOrName(r.data)
	if err != nil {
		return err
	}
	// Prevent a backward migration of label -> name. TODO: Remove this after
	// 2.0.
	olabel, nlabel := r.GetChange("label")
	if olabel != "" && nlabel == "" {
		return errors.New("cannot migrate from label to name")
	}
	// Carry forward the name attribute like we used to if no label is defined.
	// TODO: Remove this after 2.0.
	if nlabel == "" {
		oname, _ := r.GetChange("name")
		r.Set("name", oname.(string))
	}

	// set some computed fields: key, device_address, and uuid will always be
	// non-populated, so copy those.
	okey, _ := r.GetChange("key")
	odaddr, _ := r.GetChange("device_address")
	ouuid, _ := r.GetChange("uuid")
	r.Set("key", okey)
	r.Set("device_address", odaddr)
	r.Set("uuid", ouuid)

	if !r.Get("attach").(bool) {
		// Carry forward path when attach is not set
		opath, _ := r.GetChange("path")
		r.Set("path", opath.(string))
	}

	// Set the datastore if it's missing as we infer this from the default
	// datastore in that case
	if r.Get("datastore_id") == "" {
		switch {
		case r.rdd.HasChange("datastore_id"):
			// If the default datastore is changing and we don't have a default
			// datastore here, we need to use the implicit setting here to indicate
			// that we may need to migrate. This allows us to differentiate between a
			// full storage vMotion no-op, an implicit migration, and a migration
			// where we will need to generate a relocate spec for the individual disk
			// to ensure it stays at a datastore it might be pinned on.
			dsID := r.rdd.Get("datastore_id").(string)
			if dsID == "" {
				r.Set("datastore_id", diskDatastoreComputedName)
			} else {
				r.Set("datastore_id", dsID)
			}
		default:
			if err = r.normalizeDiskDatastore(); err != nil {
				return err
			}
		}
	}

	// Preserve the share value if we don't have custom shares set
	osc, _ := r.GetChange("io_share_count")
	if r.Get("io_share_level").(string) != string(types.SharesLevelCustom) {
		r.Set("io_share_count", osc)
	}

	// Ensure that the user is not attempting to shrink the disk. If we do more
	// we might want to change the name of this method, but we want to check this
	// here as CustomizeDiff is meant for vetoing.
	osize, nsize := r.GetChange("size")
	if osize.(int) > nsize.(int) {
		return fmt.Errorf("virtual disk %q: virtual disks cannot be shrunk (old: %d new: %d)", name, osize.(int), nsize.(int))
	}

	// Ensure that there is no change in either eagerly_scrub or thin_provisioned
	// - these values cannot be changed once set.
	if _, err = r.GetWithVeto("eagerly_scrub"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}
	if _, err = r.GetWithVeto("thin_provisioned"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}
	// Same with attach
	if _, err = r.GetWithVeto("attach"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}

	// Validate storage vMotion if the datastore is changing
	if r.HasChange("datastore_id") {
		if err = r.validateStorageRelocateDiff(); err != nil {
			return err
		}
	}
	log.Printf("[DEBUG] %s: Normalization of existing disk diff complete", r)
	return nil
}
// DiffExisting validates and normalizes the fields for an existing disk
// sub-resource.  It handles carrying over existing values, so this should not
// be used on disks that have not been successfully matched up between current
// and old diffs.