// readTfplan reads a protobuf-encoded description from the plan portion of
// a plan file, which is stored in a special file in the archive called
// "tfplan".
readTfplan reads a protobuf-encoded description from the plan portion of
a plan file, which is stored in a special file in the archive called
"tfplan".
// readTfplan reads a protobuf - encoded description from the plan portion of // a plan file which is stored in a special file in the archive called // " tfplan " .
func readTfplan(r io.Reader) (*plans.Plan, error) {
	src, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	var rawPlan planproto.Plan
	err = proto.Unmarshal(src, &rawPlan)
	if err != nil {
		return nil, fmt.Errorf("parse error: %s", err)
	}
	if rawPlan.Version != tfplanFormatVersion {
		return nil, fmt.Errorf("unsupported plan file format version %d; only version %d is supported", rawPlan.Version, tfplanFormatVersion)
	}
	if rawPlan.TerraformVersion != version.String() {
		return nil, fmt.Errorf("plan file was created by Terraform %s, but this is %s; plan files cannot be transferred between different Terraform versions", rawPlan.TerraformVersion, version.String())
	}
	plan := &plans.Plan{
		VariableValues: map[string]plans.DynamicValue{},
		Changes: &plans.Changes{
			Outputs:   []*plans.OutputChangeSrc{},
			Resources: []*plans.ResourceInstanceChangeSrc{},
		},
		ProviderSHA256s: map[string][]byte{},
	}
	for _, rawOC := range rawPlan.OutputChanges {
		name := rawOC.Name
		change, err := changeFromTfplan(rawOC.Change)
		if err != nil {
			return nil, fmt.Errorf("invalid plan for output %q: %s", name, err)
		}
		plan.Changes.Outputs = append(plan.Changes.Outputs, &plans.OutputChangeSrc{
			Addr:      addrs.OutputValue{Name: name}.Absolute(addrs.RootModuleInstance),
			ChangeSrc: *change,
			Sensitive: rawOC.Sensitive,
		})
	}
	for _, rawRC := range rawPlan.ResourceChanges {
		change, err := resourceChangeFromTfplan(rawRC)
		if err != nil {
			return nil, err
		}
		plan.Changes.Resources = append(plan.Changes.Resources, change)
	}
	for _, rawTargetAddr := range rawPlan.TargetAddrs {
		target, diags := addrs.ParseTargetStr(rawTargetAddr)
		if diags.HasErrors() {
			return nil, fmt.Errorf("plan contains invalid target address %q: %s", target, diags.Err())
		}
		plan.TargetAddrs = append(plan.TargetAddrs, target.Subject)
	}
	for name, rawHashObj := range rawPlan.ProviderHashes {
		if len(rawHashObj.Sha256) == 0 {
			return nil, fmt.Errorf("no SHA256 hash for provider %q plugin", name)
		}
		plan.ProviderSHA256s[name] = rawHashObj.Sha256
	}
	for name, rawVal := range rawPlan.Variables {
		val, err := valueFromTfplan(rawVal)
		if err != nil {
			return nil, fmt.Errorf("invalid value for input variable %q: %s", name, err)
		}
		plan.VariableValues[name] = val
	}
	if rawBackend := rawPlan.Backend; rawBackend == nil {
		return nil, fmt.Errorf("plan file has no backend settings; backend settings are required")
	} else {
		config, err := valueFromTfplan(rawBackend.Config)
		if err != nil {
			return nil, fmt.Errorf("plan file has invalid backend configuration: %s", err)
		}
		plan.Backend = plans.Backend{
			Type:      rawBackend.Type,
			Config:    config,
			Workspace: rawBackend.Workspace,
		}
	}
	return plan, nil
}
func readTfplan ( r io . Reader ) ( * plans . Plan , error ) { src , err := ioutil . ReadAll ( r ) if err != nil { return nil , err } var rawPlan planproto . Plan err = proto . Unmarshal ( src , & rawPlan ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if rawPlan . Version != tfplanFormatVersion { return nil , fmt . Errorf ( " " , rawPlan . Version , tfplanFormatVersion ) } if rawPlan . TerraformVersion != version . String ( ) { return nil , fmt . Errorf ( " " , rawPlan . TerraformVersion , version . String ( ) ) } plan := & plans . Plan { VariableValues : map [ string ] plans . DynamicValue { } , Changes : & plans . Changes { Outputs : [ ] * plans . OutputChangeSrc { } , Resources : [ ] * plans . ResourceInstanceChangeSrc { } , } , ProviderSHA256s : map [ string ] [ ] byte { } , } for _ , rawOC := range rawPlan . OutputChanges { name := rawOC . Name change , err := changeFromTfplan ( rawOC . Change ) if err != nil { return nil , fmt . Errorf ( " " , name , err ) } plan . Changes . Outputs = append ( plan . Changes . Outputs , & plans . OutputChangeSrc { Addr : addrs . OutputValue { Name : name } . Absolute ( addrs . RootModuleInstance ) , ChangeSrc : * change , Sensitive : rawOC . Sensitive , } ) } for _ , rawRC := range rawPlan . ResourceChanges { change , err := resourceChangeFromTfplan ( rawRC ) if err != nil { return nil , err } plan . Changes . Resources = append ( plan . Changes . Resources , change ) } for _ , rawTargetAddr := range rawPlan . TargetAddrs { target , diags := addrs . ParseTargetStr ( rawTargetAddr ) if diags . HasErrors ( ) { return nil , fmt . Errorf ( " " , target , diags . Err ( ) ) } plan . TargetAddrs = append ( plan . TargetAddrs , target . Subject ) } for name , rawHashObj := range rawPlan . ProviderHashes { if len ( rawHashObj . Sha256 ) == 0 { return nil , fmt . Errorf ( " " , name ) } plan . ProviderSHA256s [ name ] = rawHashObj . Sha256 } for name , rawVal := range rawPlan . Variables { val , err := valueFromTfplan ( rawVal ) if err != nil { return nil , fmt . Errorf ( " " , name , err ) } plan . VariableValues [ name ] = val } if rawBackend := rawPlan . Backend ; rawBackend == nil { return nil , fmt . Errorf ( " " ) } else { config , err := valueFromTfplan ( rawBackend . Config ) if err != nil { return nil , fmt . Errorf ( " " , err ) } plan . Backend = plans . Backend { Type : rawBackend . Type , Config : config , Workspace : rawBackend . Workspace , } } return plan , nil }
readTfplan reads a protobuf-encoded description from the plan portion of
a plan file, which is stored in a special file in the archive called
"tfplan".
readTfplan reads a protobuf - encoded description from the plan portion of a plan file which is stored in a special file in the archive called " tfplan " .
// All output values saved in the plan file are root module outputs, // since we don't retain others. (They can be easily recomputed // during apply). // errors from resourceChangeFromTfplan already include context