예제 #1
0
func (m schemaMap) diff(
	k string,
	schema *Schema,
	diff *terraform.InstanceDiff,
	d *ResourceData,
	all bool) error {

	unsupressedDiff := new(terraform.InstanceDiff)
	unsupressedDiff.Attributes = make(map[string]*terraform.ResourceAttrDiff)

	var err error
	switch schema.Type {
	case TypeBool, TypeInt, TypeFloat, TypeString:
		err = m.diffString(k, schema, unsupressedDiff, d, all)
	case TypeList:
		err = m.diffList(k, schema, unsupressedDiff, d, all)
	case TypeMap:
		err = m.diffMap(k, schema, unsupressedDiff, d, all)
	case TypeSet:
		err = m.diffSet(k, schema, unsupressedDiff, d, all)
	default:
		err = fmt.Errorf("%s: unknown type %#v", k, schema.Type)
	}

	for attrK, attrV := range unsupressedDiff.Attributes {
		if schema.DiffSuppressFunc != nil && schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, d) {
			continue
		}

		diff.Attributes[attrK] = attrV
	}

	return err
}
예제 #2
0
func (m schemaMap) diffSet(
	k string,
	schema *Schema,
	diff *terraform.InstanceDiff,
	d *ResourceData,
	all bool) error {
	if !all {
		// This is a bit strange, but we expect the entire set to be in the diff,
		// so we first diff the set normally but with a new diff. Then, if
		// there IS any change, we just set the change to the entire list.
		tempD := new(terraform.InstanceDiff)
		tempD.Attributes = make(map[string]*terraform.ResourceAttrDiff)
		if err := m.diffList(k, schema, tempD, d, false); err != nil {
			return err
		}

		// If we had no changes, then we're done
		if tempD.Empty() {
			return nil
		}
	}

	// We have changes, so re-run the diff, but set a flag to force
	// getting all diffs, even if there is no change.
	return m.diffList(k, schema, diff, d, true)
}
예제 #3
0
func testResourceDiffStr(rd *terraform.InstanceDiff) string {
	var buf bytes.Buffer

	crud := "UPDATE"
	if rd.RequiresNew() {
		crud = "CREATE"
	}

	buf.WriteString(fmt.Sprintf(
		"%s\n",
		crud))

	keyLen := 0
	keys := make([]string, 0, len(rd.Attributes))
	for key, _ := range rd.Attributes {
		keys = append(keys, key)
		if len(key) > keyLen {
			keyLen = len(key)
		}
	}
	sort.Strings(keys)

	for _, attrK := range keys {
		attrDiff := rd.Attributes[attrK]

		v := attrDiff.New
		if attrDiff.NewComputed {
			v = "<computed>"
		}
		if attrDiff.NewRemoved {
			v = "<removed>"
		}

		newResource := ""
		if attrDiff.RequiresNew {
			newResource = " (forces new resource)"
		}

		inOut := "IN "
		if attrDiff.Type == terraform.DiffAttrOutput {
			inOut = "OUT"
		}

		buf.WriteString(fmt.Sprintf(
			"  %s %s:%s %#v => %#v%s\n",
			inOut,
			attrK,
			strings.Repeat(" ", keyLen-len(attrK)),
			attrDiff.Old,
			v,
			newResource))
	}

	return buf.String()
}
예제 #4
0
// Apply creates, updates, and/or deletes a resource.
func (r *Resource) Apply(
	s *terraform.InstanceState,
	d *terraform.InstanceDiff,
	meta interface{}) (*terraform.InstanceState, error) {
	data, err := schemaMap(r.Schema).Data(s, d)
	if err != nil {
		return s, err
	}

	if s == nil {
		// The Terraform API dictates that this should never happen, but
		// it doesn't hurt to be safe in this case.
		s = new(terraform.InstanceState)
	}

	if d.Destroy || d.RequiresNew() {
		if s.ID != "" {
			// Destroy the resource since it is created
			if err := r.Delete(data, meta); err != nil {
				return r.recordCurrentSchemaVersion(data.State()), err
			}

			// Make sure the ID is gone.
			data.SetId("")
		}

		// If we're only destroying, and not creating, then return
		// now since we're done!
		if !d.RequiresNew() {
			return nil, nil
		}

		// Reset the data to be stateless since we just destroyed
		data, err = schemaMap(r.Schema).Data(nil, d)
		if err != nil {
			return nil, err
		}
	}

	err = nil
	if data.Id() == "" {
		// We're creating, it is a new resource.
		data.MarkNewResource()
		err = r.Create(data, meta)
	} else {
		if r.Update == nil {
			return s, fmt.Errorf("doesn't support update")
		}

		err = r.Update(data, meta)
	}

	return r.recordCurrentSchemaVersion(data.State()), err
}
예제 #5
0
// Apply performs a create or update depending on the diff, and calls
// the proper function on the matching Resource.
func (m *Map) Apply(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	d *terraform.InstanceDiff,
	meta interface{}) (*terraform.InstanceState, error) {
	r, ok := m.Mapping[info.Type]
	if !ok {
		return nil, fmt.Errorf("Unknown resource type: %s", info.Type)
	}

	if d.Destroy || d.RequiresNew() {
		if s.ID != "" {
			// Destroy the resource if it is created
			err := r.Destroy(s, meta)
			if err != nil {
				return s, err
			}

			s.ID = ""
		}

		// If we're only destroying, and not creating, then return now.
		// Otherwise, we continue so that we can create a new resource.
		if !d.RequiresNew() {
			return nil, nil
		}
	}

	var result *terraform.InstanceState
	var err error
	if s.ID == "" {
		result, err = r.Create(s, d, meta)
	} else {
		if r.Update == nil {
			return s, fmt.Errorf(
				"Resource type '%s' doesn't support update",
				info.Type)
		}

		result, err = r.Update(s, d, meta)
	}
	if result != nil {
		if result.Attributes == nil {
			result.Attributes = make(map[string]string)
		}

		result.Attributes["id"] = result.ID
	}

	return result, err
}
예제 #6
0
func (h *CountHook) PostDiff(
	n *terraform.InstanceInfo, d *terraform.InstanceDiff) (
	terraform.HookAction, error) {
	h.Lock()
	defer h.Unlock()

	switch d.ChangeType() {
	case terraform.DiffDestroyCreate:
		h.ToRemoveAndAdd += 1
	case terraform.DiffCreate:
		h.ToAdd += 1
	case terraform.DiffDestroy:
		h.ToRemove += 1
	case terraform.DiffUpdate:
		h.ToChange += 1
	}

	return terraform.HookActionContinue, nil
}
예제 #7
0
func (h *CountHook) PreApply(
	n *terraform.InstanceInfo,
	s *terraform.InstanceState,
	d *terraform.InstanceDiff) (terraform.HookAction, error) {
	h.Lock()
	defer h.Unlock()

	if h.pending == nil {
		h.pending = make(map[string]countHookAction)
	}

	action := countHookActionChange
	if d.GetDestroy() {
		action = countHookActionRemove
	} else if s.ID == "" {
		action = countHookActionAdd
	}

	h.pending[n.HumanId()] = action

	return terraform.HookActionContinue, nil
}
예제 #8
0
func (h *CountHook) PostDiff(
	n *terraform.InstanceInfo, d *terraform.InstanceDiff) (
	terraform.HookAction, error) {
	h.Lock()
	defer h.Unlock()

	// We don't count anything for data sources
	if strings.HasPrefix(n.Id, "data.") {
		return terraform.HookActionContinue, nil
	}

	switch d.ChangeType() {
	case terraform.DiffDestroyCreate:
		h.ToRemoveAndAdd += 1
	case terraform.DiffCreate:
		h.ToAdd += 1
	case terraform.DiffDestroy:
		h.ToRemove += 1
	case terraform.DiffUpdate:
		h.ToChange += 1
	}

	return terraform.HookActionContinue, nil
}
예제 #9
0
파일: schema.go 프로젝트: ack/terraform
// Diff returns the diff for a resource given the schema map,
// state, and configuration.
func (m schemaMap) Diff(
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
	result := new(terraform.InstanceDiff)
	result.Attributes = make(map[string]*terraform.ResourceAttrDiff)

	d := &ResourceData{
		schema: m,
		state:  s,
		config: c,
	}

	for k, schema := range m {
		err := m.diff(k, schema, result, d, false)
		if err != nil {
			return nil, err
		}
	}

	// If the diff requires a new resource, then we recompute the diff
	// so we have the complete new resource diff, and preserve the
	// RequiresNew fields where necessary so the user knows exactly what
	// caused that.
	if result.RequiresNew() {
		// Create the new diff
		result2 := new(terraform.InstanceDiff)
		result2.Attributes = make(map[string]*terraform.ResourceAttrDiff)

		// Reset the data to not contain state. We have to call init()
		// again in order to reset the FieldReaders.
		d.state = nil
		d.init()

		// Perform the diff again
		for k, schema := range m {
			err := m.diff(k, schema, result2, d, false)
			if err != nil {
				return nil, err
			}
		}

		// Force all the fields to not force a new since we know what we
		// want to force new.
		for k, attr := range result2.Attributes {
			if attr == nil {
				continue
			}

			if attr.RequiresNew {
				attr.RequiresNew = false
			}

			if s != nil {
				attr.Old = s.Attributes[k]
			}
		}

		// Now copy in all the requires new diffs...
		for k, attr := range result.Attributes {
			if attr == nil {
				continue
			}

			newAttr, ok := result2.Attributes[k]
			if !ok {
				newAttr = attr
			}

			if attr.RequiresNew {
				newAttr.RequiresNew = true
			}

			result2.Attributes[k] = newAttr
		}

		// And set the diff!
		result = result2
	}

	// Remove any nil diffs just to keep things clean
	for k, v := range result.Attributes {
		if v == nil {
			delete(result.Attributes, k)
		}
	}

	// Go through and detect all of the ComputedWhens now that we've
	// finished the diff.
	// TODO

	if result.Empty() {
		// If we don't have any diff elements, just return nil
		return nil, nil
	}

	return result, nil
}
예제 #10
0
func (h *UiHook) PreApply(
	n *terraform.InstanceInfo,
	s *terraform.InstanceState,
	d *terraform.InstanceDiff) (terraform.HookAction, error) {
	h.once.Do(h.init)

	id := n.HumanId()

	op := uiResourceModify
	if d.Destroy {
		op = uiResourceDestroy
	} else if s.ID == "" {
		op = uiResourceCreate
	}

	h.l.Lock()
	h.resources[id] = uiResourceState{
		Op:    op,
		Start: time.Now().Round(time.Second),
	}
	h.l.Unlock()

	var operation string
	switch op {
	case uiResourceModify:
		operation = "Modifying..."
	case uiResourceDestroy:
		operation = "Destroying..."
	case uiResourceCreate:
		operation = "Creating..."
	case uiResourceUnknown:
		return terraform.HookActionContinue, nil
	}

	attrBuf := new(bytes.Buffer)

	// Get all the attributes that are changing, and sort them. Also
	// determine the longest key so that we can align them all.
	keyLen := 0

	dAttrs := d.CopyAttributes()
	keys := make([]string, 0, len(dAttrs))
	for key, _ := range dAttrs {
		// Skip the ID since we do that specially
		if key == "id" {
			continue
		}

		keys = append(keys, key)
		if len(key) > keyLen {
			keyLen = len(key)
		}
	}
	sort.Strings(keys)

	// Go through and output each attribute
	for _, attrK := range keys {
		attrDiff, _ := d.GetAttribute(attrK)

		v := attrDiff.New
		u := attrDiff.Old
		if attrDiff.NewComputed {
			v = "<computed>"
		}

		if attrDiff.Sensitive {
			u = "<sensitive>"
			v = "<sensitive>"
		}

		attrBuf.WriteString(fmt.Sprintf(
			"  %s:%s %#v => %#v\n",
			attrK,
			strings.Repeat(" ", keyLen-len(attrK)),
			u,
			v))
	}

	attrString := strings.TrimSpace(attrBuf.String())
	if attrString != "" {
		attrString = "\n  " + attrString
	}

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: %s[reset_bold]%s",
		id,
		operation,
		attrString)))

	// Set a timer to show an operation is still happening
	time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id) })

	return terraform.HookActionContinue, nil
}