mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
Merge pull request #7989 from hashicorp/jbardin/tf_push
Override atlas variables even if they aren't local
This commit is contained in:
commit
1322aaf473
@ -47,6 +47,22 @@ func (c *PushCommand) Run(args []string) int {
|
|||||||
overwriteMap[v] = struct{}{}
|
overwriteMap[v] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a map of variables specifically from the CLI that we want to overwrite.
|
||||||
|
// We need this because there is a chance that the user is trying to modify
|
||||||
|
// a variable we don't see in our context, but which exists in this atlas
|
||||||
|
// environment.
|
||||||
|
cliVars := make(map[string]string)
|
||||||
|
for k, v := range c.variables {
|
||||||
|
if _, ok := overwriteMap[k]; ok {
|
||||||
|
if val, ok := v.(string); ok {
|
||||||
|
cliVars[k] = val
|
||||||
|
} else {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error reading value for variable: %s", k))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The pwd is used for the configuration path if one is not given
|
// The pwd is used for the configuration path if one is not given
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -145,19 +161,14 @@ func (c *PushCommand) Run(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter any overwrites from the atlas vars
|
|
||||||
for k := range overwriteMap {
|
|
||||||
delete(atlasVars, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set remote variables in the context if we don't have a value here. These
|
// Set remote variables in the context if we don't have a value here. These
|
||||||
// don't have to be correct, it just prevents the Input walk from prompting
|
// don't have to be correct, it just prevents the Input walk from prompting
|
||||||
// the user for input, The atlas variable may be an hcl-encoded object, but
|
// the user for input.
|
||||||
// we're just going to set it as the raw string value.
|
|
||||||
ctxVars := ctx.Variables()
|
ctxVars := ctx.Variables()
|
||||||
for k, av := range atlasVars {
|
atlasVarSentry := "ATLAS_78AC153CA649EAA44815DAD6CBD4816D"
|
||||||
|
for k, _ := range atlasVars {
|
||||||
if _, ok := ctxVars[k]; !ok {
|
if _, ok := ctxVars[k]; !ok {
|
||||||
ctx.SetVariable(k, av.Value)
|
ctx.SetVariable(k, atlasVarSentry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,23 +214,47 @@ func (c *PushCommand) Run(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output to the user the variables that will be uploaded
|
// List of the vars we're uploading to display to the user.
|
||||||
|
// We always upload all vars from atlas, but only report them if they are overwritten.
|
||||||
var setVars []string
|
var setVars []string
|
||||||
|
|
||||||
// variables to upload
|
// variables to upload
|
||||||
var uploadVars []atlas.TFVar
|
var uploadVars []atlas.TFVar
|
||||||
|
|
||||||
// Now we can combine the vars for upload to atlas and list the variables
|
// first add all the variables we want to send which have been serialized
|
||||||
// we're uploading for the user
|
// from the local context.
|
||||||
for _, sv := range serializedVars {
|
for _, sv := range serializedVars {
|
||||||
if av, ok := atlasVars[sv.Key]; ok {
|
_, inOverwrite := overwriteMap[sv.Key]
|
||||||
// this belongs to Atlas
|
_, inAtlas := atlasVars[sv.Key]
|
||||||
uploadVars = append(uploadVars, av)
|
|
||||||
} else {
|
// We have a variable that's not in atlas, so always send it.
|
||||||
// we're uploading our local version
|
if !inAtlas {
|
||||||
setVars = append(setVars, sv.Key)
|
|
||||||
uploadVars = append(uploadVars, sv)
|
uploadVars = append(uploadVars, sv)
|
||||||
|
setVars = append(setVars, sv.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We're overwriting an atlas variable.
|
||||||
|
// We also want to check that we
|
||||||
|
// don't send the dummy sentry value back to atlas. This could happen
|
||||||
|
// if it's specified as an overwrite on the cli, but we didn't set a
|
||||||
|
// new value.
|
||||||
|
if inAtlas && inOverwrite && sv.Value != atlasVarSentry {
|
||||||
|
uploadVars = append(uploadVars, sv)
|
||||||
|
setVars = append(setVars, sv.Key)
|
||||||
|
|
||||||
|
// remove this value from the atlas vars, because we're going to
|
||||||
|
// send back the remainder regardless.
|
||||||
|
delete(atlasVars, sv.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now send back all the existing atlas vars, inserting any overwrites from the cli.
|
||||||
|
for k, av := range atlasVars {
|
||||||
|
if v, ok := cliVars[k]; ok {
|
||||||
|
av.Value = v
|
||||||
|
setVars = append(setVars, k)
|
||||||
|
}
|
||||||
|
uploadVars = append(uploadVars, av)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(setVars)
|
sort.Strings(setVars)
|
||||||
|
@ -264,6 +264,97 @@ func TestPush_localOverride(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This tests that the push command will override Atlas variables
|
||||||
|
// even if we don't have it defined locally
|
||||||
|
func TestPush_remoteOverride(t *testing.T) {
|
||||||
|
// Disable test mode so input would be asked and setup the
|
||||||
|
// input reader/writers.
|
||||||
|
test = false
|
||||||
|
defer func() { test = true }()
|
||||||
|
defaultInputReader = bytes.NewBufferString("nope\n")
|
||||||
|
defaultInputWriter = new(bytes.Buffer)
|
||||||
|
|
||||||
|
tmp, cwd := testCwd(t)
|
||||||
|
defer testFixCwd(t, tmp, cwd)
|
||||||
|
|
||||||
|
// Create remote state file, this should be pulled
|
||||||
|
conf, srv := testRemoteState(t, testState(), 200)
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
// Persist local remote state
|
||||||
|
s := terraform.NewState()
|
||||||
|
s.Serial = 5
|
||||||
|
s.Remote = conf
|
||||||
|
testStateFileRemote(t, s)
|
||||||
|
|
||||||
|
// Path where the archive will be "uploaded" to
|
||||||
|
archivePath := testTempFile(t)
|
||||||
|
defer os.Remove(archivePath)
|
||||||
|
|
||||||
|
client := &mockPushClient{File: archivePath}
|
||||||
|
// Provided vars should override existing ones
|
||||||
|
client.GetResult = map[string]atlas.TFVar{
|
||||||
|
"remote": atlas.TFVar{
|
||||||
|
Key: "remote",
|
||||||
|
Value: "old",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &PushCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
path := testFixturePath("push-tfvars")
|
||||||
|
args := []string{
|
||||||
|
"-var-file", path + "/terraform.tfvars",
|
||||||
|
"-vcs=false",
|
||||||
|
"-overwrite=remote",
|
||||||
|
"-var",
|
||||||
|
"remote=new",
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := testArchiveStr(t, archivePath)
|
||||||
|
expected := []string{
|
||||||
|
".terraform/",
|
||||||
|
".terraform/terraform.tfstate",
|
||||||
|
"main.tf",
|
||||||
|
"terraform.tfvars",
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("bad: %#v", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.UpsertOptions.Name != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", client.UpsertOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
// find the "remote" var and make sure we're going to set it
|
||||||
|
for _, tfVar := range client.UpsertOptions.TFVars {
|
||||||
|
if tfVar.Key == "remote" {
|
||||||
|
found = true
|
||||||
|
if tfVar.Value != "new" {
|
||||||
|
t.Log("'remote' variable should be set to 'new'")
|
||||||
|
t.Fatalf("sending instead: %#v", tfVar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Fatal("'remote' variable not being sent to atlas")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This tests that the push command prefers Atlas variables over
|
// This tests that the push command prefers Atlas variables over
|
||||||
// local ones.
|
// local ones.
|
||||||
func TestPush_preferAtlas(t *testing.T) {
|
func TestPush_preferAtlas(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user