Browse Source
https://github.com/golang/go/issues/7101 > cmd/gofmt: remove -tabs and -tabwidth flags #7101 https://stackoverflow.blog/2017/06/15/developers-use-spaces-make-money-use-tabs/ > Developers Who Use Spaces Make More Money Than Those Who Use Tabs googles trying to take all our money guysmaster
18 changed files with 3185 additions and 3143 deletions
@ -1,117 +1,117 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"encoding/json" |
||||
"fmt" |
||||
"os" |
||||
"os/exec" |
||||
"strings" |
||||
|
||||
errors "git.sequentialread.com/forest/pkg-errors" |
||||
"bufio" |
||||
"encoding/json" |
||||
"fmt" |
||||
"os" |
||||
"os/exec" |
||||
"strings" |
||||
|
||||
errors "git.sequentialread.com/forest/pkg-errors" |
||||
) |
||||
|
||||
type AnsibleTaskResult struct { |
||||
Name string |
||||
Success bool |
||||
Skipped bool |
||||
Mode string |
||||
Role string |
||||
Changed bool |
||||
Name string |
||||
Success bool |
||||
Skipped bool |
||||
Mode string |
||||
Role string |
||||
Changed bool |
||||
} |
||||
|
||||
func main() { |
||||
arguments := os.Args[1:] |
||||
process := exec.Command("ansible-playbook", arguments...) |
||||
stdoutPipe, err := process.StdoutPipe() |
||||
if err != nil { |
||||
fmt.Fprint(os.Stderr, "can't connect stdout pipe to ansible-playbook process") |
||||
os.Exit(1) |
||||
} |
||||
stderrPipe, err := process.StderrPipe() |
||||
if err != nil { |
||||
fmt.Fprint(os.Stderr, "can't connect stderr pipe to ansible-playbook process") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
err = process.Start() |
||||
if err != nil { |
||||
err = errors.Wrapf(err, "can't ShellExec(ansible-playbook %s), process.Start() returned", strings.Join(arguments, " ")) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
stdoutScanner := bufio.NewScanner(stdoutPipe) |
||||
stdoutScanner.Split(bufio.ScanLines) |
||||
|
||||
stderrScanner := bufio.NewScanner(stderrPipe) |
||||
stderrScanner.Split(bufio.ScanLines) |
||||
|
||||
_ = os.Remove("ansible.log") |
||||
_ = os.Remove("ansible-log.json") |
||||
|
||||
ansibleTaskResults := []AnsibleTaskResult{} |
||||
|
||||
writeLogLine := func(text string) error { |
||||
file, err := os.OpenFile("ansible.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
defer file.Close() |
||||
|
||||
if _, err = file.WriteString(fmt.Sprintln(text)); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
writeTaskResult := func(taskResult AnsibleTaskResult) error { |
||||
ansibleTaskResults = append(ansibleTaskResults, taskResult) |
||||
file, err := os.OpenFile("ansible-log.json", os.O_CREATE|os.O_WRONLY, 0600) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer file.Close() |
||||
jsonBytes, err := json.MarshalIndent(ansibleTaskResults, "", " ") |
||||
if err != nil { |
||||
return err |
||||
} |
||||
_, err = file.Write(jsonBytes) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
go (func() { |
||||
for stdoutScanner.Scan() { |
||||
line := stdoutScanner.Text() |
||||
var taskResult AnsibleTaskResult |
||||
err := json.Unmarshal([]byte(line), &taskResult) |
||||
if err == nil && taskResult.Name != "" { |
||||
err = writeTaskResult(taskResult) |
||||
} else { |
||||
err = writeLogLine(fmt.Sprintf("stdout: %s", line)) |
||||
} |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
go (func() { |
||||
for stderrScanner.Scan() { |
||||
err := writeLogLine(fmt.Sprintf("stderr: %s", stderrScanner.Text())) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
err = process.Wait() |
||||
if err != nil { |
||||
err = errors.Wrapf(err, "can't ShellExec(ansible-playbook %s), process.Wait() returned", strings.Join(arguments, " ")) |
||||
} |
||||
|
||||
os.Exit(process.ProcessState.ExitCode()) |
||||
arguments := os.Args[1:] |
||||
process := exec.Command("ansible-playbook", arguments...) |
||||
stdoutPipe, err := process.StdoutPipe() |
||||
if err != nil { |
||||
fmt.Fprint(os.Stderr, "can't connect stdout pipe to ansible-playbook process") |
||||
os.Exit(1) |
||||
} |
||||
stderrPipe, err := process.StderrPipe() |
||||
if err != nil { |
||||
fmt.Fprint(os.Stderr, "can't connect stderr pipe to ansible-playbook process") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
err = process.Start() |
||||
if err != nil { |
||||
err = errors.Wrapf(err, "can't ShellExec(ansible-playbook %s), process.Start() returned", strings.Join(arguments, " ")) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
stdoutScanner := bufio.NewScanner(stdoutPipe) |
||||
stdoutScanner.Split(bufio.ScanLines) |
||||
|
||||
stderrScanner := bufio.NewScanner(stderrPipe) |
||||
stderrScanner.Split(bufio.ScanLines) |
||||
|
||||
_ = os.Remove("ansible.log") |
||||
_ = os.Remove("ansible-log.json") |
||||
|
||||
ansibleTaskResults := []AnsibleTaskResult{} |
||||
|
||||
writeLogLine := func(text string) error { |
||||
file, err := os.OpenFile("ansible.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
defer file.Close() |
||||
|
||||
if _, err = file.WriteString(fmt.Sprintln(text)); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
writeTaskResult := func(taskResult AnsibleTaskResult) error { |
||||
ansibleTaskResults = append(ansibleTaskResults, taskResult) |
||||
file, err := os.OpenFile("ansible-log.json", os.O_CREATE|os.O_WRONLY, 0600) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer file.Close() |
||||
jsonBytes, err := json.MarshalIndent(ansibleTaskResults, "", " ") |
||||
if err != nil { |
||||
return err |
||||
} |
||||
_, err = file.Write(jsonBytes) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
go (func() { |
||||
for stdoutScanner.Scan() { |
||||
line := stdoutScanner.Text() |
||||
var taskResult AnsibleTaskResult |
||||
err := json.Unmarshal([]byte(line), &taskResult) |
||||
if err == nil && taskResult.Name != "" { |
||||
err = writeTaskResult(taskResult) |
||||
} else { |
||||
err = writeLogLine(line) |
||||
} |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
go (func() { |
||||
for stderrScanner.Scan() { |
||||
err := writeLogLine(stderrScanner.Text()) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
err = process.Wait() |
||||
if err != nil { |
||||
err = errors.Wrapf(err, "can't ShellExec(ansible-playbook %s), process.Wait() returned", strings.Join(arguments, " ")) |
||||
} |
||||
|
||||
os.Exit(process.ProcessState.ExitCode()) |
||||
} |
||||
|
@ -1,113 +1,113 @@
|
||||
package automation |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
errors "git.sequentialread.com/forest/pkg-errors" |
||||
"git.sequentialread.com/forest/rootsystem/configuration" |
||||
"encoding/json" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
errors "git.sequentialread.com/forest/pkg-errors" |
||||
"git.sequentialread.com/forest/rootsystem/configuration" |
||||
) |
||||
|
||||
type digialOceanSSHKeysResponse struct { |
||||
SSHKeys []digitalOceanSSHKey `json:"ssh_keys"` |
||||
SSHKeys []digitalOceanSSHKey `json:"ssh_keys"` |
||||
} |
||||
|
||||
type digitalOceanSSHKey struct { |
||||
Id int `json:"id"` |
||||
Fingerprint string `json:"fingerprint"` |
||||
PublicKey string `json:"public_key"` |
||||
Pame string `json:"name"` |
||||
Id int `json:"id"` |
||||
Fingerprint string `json:"fingerprint"` |
||||
PublicKey string `json:"public_key"` |
||||
Pame string `json:"name"` |
||||
} |
||||
|
||||
func handleDigitalOceanSSHKeyAlreadyExists( |
||||
config *configuration.Configuration, |
||||
workingDirectory string, |
||||
terraformDirectory string, |
||||
tfShow *TerraformShow, |
||||
config *configuration.Configuration, |
||||
workingDirectory string, |
||||
terraformDirectory string, |
||||
tfShow *TerraformShow, |
||||
) (bool, error) { |
||||
|
||||
var digitaloceanCredential *configuration.Credential = nil |
||||
for _, cred := range config.Credentials { |
||||
if cred.Type == configuration.DIGITALOCEAN { |
||||
digitaloceanCredential = &cred |
||||
break |
||||
} |
||||
} |
||||
|
||||
createSSHKeys := filterResourceChanges( |
||||
tfShow, |
||||
ResourceChangeFilter{ResourceType: "digitalocean_ssh_key", Action: "create", NotAction: "delete"}, |
||||
) |
||||
|
||||
if digitaloceanCredential == nil || len(createSSHKeys) == 0 { |
||||
return false, nil |
||||
} |
||||
|
||||
// createAnySSHKeys
|
||||
// for _, module := range tfShow.PlannedValues.RootModule.ChildModules {
|
||||
// for _, resource := range module.Resources {
|
||||
// if resource.Type == "digitalocean_ssh_key" {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Get the ssh public keys from digital ocean API
|
||||
httpClient := http.Client{} |
||||
request, err := http.NewRequest("GET", fmt.Sprintf("%s%s", configuration.DIGITALOCEAN_API_URL, "/v2/account/keys"), nil) |
||||
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", digitaloceanCredential.Password)) |
||||
response, err := httpClient.Do(request) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if response.StatusCode != 200 { |
||||
return false, fmt.Errorf("HTTP %d when calling /v2/account/keys on digitalocean API", response.StatusCode) |
||||
} |
||||
bytes, err := ioutil.ReadAll(response.Body) |
||||
if err != nil { |
||||
return false, errors.Wrap(err, "HTTP read error when calling /v2/account/keys on digitalocean API") |
||||
} |
||||
var responseObject digialOceanSSHKeysResponse |
||||
err = json.Unmarshal(bytes, &responseObject) |
||||
if err != nil { |
||||
return false, errors.Wrap(err, "JSON parse error when calling /v2/account/keys on digitalocean API") |
||||
} |
||||
|
||||
digitalOceanIdByKeyContent := map[string]int{} |
||||
|
||||
for _, sshKey := range responseObject.SSHKeys { |
||||
digitalOceanIdByKeyContent[strings.TrimSpace(sshKey.PublicKey)] = sshKey.Id |
||||
} |
||||
|
||||
importedAnySSHKeys := false |
||||
|
||||
// for each local SSH key which tf plans to create, check if it already exists in DO
|
||||
for _, resource := range createSSHKeys { |
||||
sshKeyContent, hasContent := getStringValue(resource, "public_key") |
||||
if !hasContent { |
||||
return false, fmt.Errorf("digitalocean_ssh_key %s (from terraform planned_values) is missing string value \"public_key\" ", resource.Address) |
||||
} |
||||
digitalOceanId, has := digitalOceanIdByKeyContent[strings.TrimSpace(sshKeyContent)] |
||||
if has { |
||||
fmt.Println("---------------------------------------") |
||||
fmt.Printf( |
||||
"Terraform plans to add the local ssh public key %s to digitalocean, but digitalocean already has that key \n", |
||||
resource.Values["name"], |
||||
) |
||||
exitCode := removeAndImportResource(terraformDirectory, resource.Address, strconv.Itoa(digitalOceanId)) |
||||
|
||||
if exitCode != 0 { |
||||
return false, errors.New("terraform import returned a non-zero exit code") |
||||
} |
||||
importedAnySSHKeys = true |
||||
|
||||
fmt.Println("---------------------------------------") |
||||
} |
||||
|
||||
} |
||||
|
||||
return importedAnySSHKeys, nil |
||||
var digitaloceanCredential *configuration.Credential = nil |
||||
for _, cred := range config.Credentials { |
||||
if cred.Type == configuration.DIGITALOCEAN { |
||||
digitaloceanCredential = &cred |
||||
break |
||||
} |
||||
} |
||||
|
||||
createSSHKeys := filterResourceChanges( |
||||
tfShow, |
||||
ResourceChangeFilter{ResourceType: "digitalocean_ssh_key", Action: "create", NotAction: "delete"}, |
||||
) |
||||
|
||||
if digitaloceanCredential == nil || len(createSSHKeys) == 0 { |
||||
return false, nil |
||||
} |
||||
|
||||
// createAnySSHKeys
|
||||
// for _, module := range tfShow.PlannedValues.RootModule.ChildModules {
|
||||
// for _, resource := range module.Resources {
|
||||
// if resource.Type == "digitalocean_ssh_key" {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Get the ssh public keys from digital ocean API
|
||||
httpClient := http.Client{} |
||||
request, err := http.NewRequest("GET", fmt.Sprintf("%s%s", configuration.DIGITALOCEAN_API_URL, "/v2/account/keys"), nil) |
||||
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", digitaloceanCredential.Password)) |
||||
response, err := httpClient.Do(request) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if response.StatusCode != 200 { |
||||
return false, fmt.Errorf("HTTP %d when calling /v2/account/keys on digitalocean API", response.StatusCode) |
||||
} |
||||
bytes, err := ioutil.ReadAll(response.Body) |
||||
if err != nil { |
||||
return false, errors.Wrap(err, "HTTP read error when calling /v2/account/keys on digitalocean API") |
||||
} |
||||
var responseObject digialOceanSSHKeysResponse |
||||
err = json.Unmarshal(bytes, &responseObject) |
||||
if err != nil { |
||||
return false, errors.Wrap(err, "JSON parse error when calling /v2/account/keys on digitalocean API") |
||||
} |
||||
|
||||
digitalOceanIdByKeyContent := map[string]int{} |
||||
|
||||
for _, sshKey := range responseObject.SSHKeys { |
||||
digitalOceanIdByKeyContent[strings.TrimSpace(sshKey.PublicKey)] = sshKey.Id |
||||
} |
||||
|
||||
importedAnySSHKeys := false |
||||
|
||||
// for each local SSH key which tf plans to create, check if it already exists in DO
|
||||
for _, resource := range createSSHKeys { |
||||
sshKeyContent, hasContent := getStringValue(resource, "public_key") |
||||
if !hasContent { |
||||
return false, fmt.Errorf("digitalocean_ssh_key %s (from terraform planned_values) is missing string value \"public_key\" ", resource.Address) |
||||
} |
||||
digitalOceanId, has := digitalOceanIdByKeyContent[strings.TrimSpace(sshKeyContent)] |
||||
if has { |
||||
fmt.Println("---------------------------------------") |
||||
fmt.Printf( |
||||
"Terraform plans to add the local ssh public key %s to digitalocean, but digitalocean already has that key \n", |
||||
resource.Values["name"], |
||||
) |
||||
exitCode := removeAndImportResource(terraformDirectory, resource.Address, strconv.Itoa(digitalOceanId)) |
||||
|
||||
if exitCode != 0 { |
||||
return false, errors.New("terraform import returned a non-zero exit code") |
||||
} |
||||
importedAnySSHKeys = true |
||||
|
||||
fmt.Println("---------------------------------------") |
||||
} |
||||
|
||||
} |
||||
|
||||
return importedAnySSHKeys, nil |
||||
} |
||||
|
@ -1,149 +1,149 @@
|
||||
package automation |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"encoding/json" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
|
||||
errors "git.sequentialread.com/forest/pkg-errors" |
||||
"git.sequentialread.com/forest/rootsystem/configuration" |
||||
errors "git.sequentialread.com/forest/pkg-errors" |
||||
"git.sequentialread.com/forest/rootsystem/configuration" |
||||
) |
||||
|
||||
// https://api.gandi.net/docs/livedns/
|
||||
type gandiLiveDNSRecord struct { |
||||
Name string `json:"rrset_name"` |
||||
Type string `json:"rrset_type"` |
||||
Values []string `json:"rrset_values"` |
||||
Name string `json:"rrset_name"` |
||||
Type string `json:"rrset_type"` |
||||
Values []string `json:"rrset_values"` |
||||
} |
||||
|
||||
func handleGandiLiveDNSRecordAlreadyExists( |
||||
config *configuration.Configuration, |
||||
workingDirectory string, |
||||
terraformDirectory string, |
||||
tfShow *TerraformShow, |
||||
config *configuration.Configuration, |
||||
workingDirectory string, |
||||
terraformDirectory string, |
||||
tfShow *TerraformShow, |
||||
) (bool, error) { |
||||
|
||||
var gandiCredential *configuration.Credential = nil |
||||
for _, cred := range config.Credentials { |
||||
if cred.Type == configuration.GANDI { |
||||
gandiCredential = &cred |
||||
break |
||||
} |
||||
} |
||||
|
||||
createLiveDNSRecords := filterResourceChanges( |
||||
tfShow, |
||||
ResourceChangeFilter{ResourceType: "gandi_livedns_record", Action: "create", NotAction: "delete"}, |
||||
) |
||||
|
||||
domainName, hasDomainName := config.Terraform.Variables["domain_name"] |
||||
|
||||
if gandiCredential == nil || len(createLiveDNSRecords) == 0 || !hasDomainName { |
||||
return false, nil |
||||
} |
||||
|
||||
// Get the list of DNS records from the Gandi API
|
||||
httpClient := http.Client{} |
||||
gandiURL := fmt.Sprintf( |
||||
"%s/%s/%s/%s", |
||||
configuration.GANDI_API_URL, "v5/livedns/domains", domainName, "records", |
||||
) |
||||
request, err := http.NewRequest("GET", gandiURL, nil) |
||||
|
||||
request.Header.Add("Authorization", fmt.Sprintf("Apikey %s", gandiCredential.Password)) |
||||
response, err := httpClient.Do(request) |
||||
|
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if response.StatusCode != 200 { |
||||
return false, fmt.Errorf("HTTP %d when calling %s ", gandiURL, response.StatusCode) |
||||
} |
||||
bytes, err := ioutil.ReadAll(response.Body) |
||||
if err != nil { |
||||
return false, errors.Wrapf(err, "HTTP read error when calling %s ", gandiURL) |
||||
} |
||||
var recordsFromAPI []gandiLiveDNSRecord |
||||
err = json.Unmarshal(bytes, &recordsFromAPI) |
||||
if err != nil { |
||||
return false, errors.Wrapf(err, "JSON parse error when calling %s ", gandiURL) |
||||
} |
||||
|
||||
recordsFromAPIById := map[string]gandiLiveDNSRecord{} |
||||
|
||||
for _, record := range recordsFromAPI { |
||||
recordsFromAPIById[fmt.Sprintf("%s/%s/%s", domainName, record.Name, record.Type)] = record |
||||
} |
||||
|
||||
importedAnyRecords := false |
||||
|
||||
// for each record terraform plans to create, check if it already exists in Gandi
|
||||
for _, resource := range createLiveDNSRecords { |
||||
recordName, hasName := getStringValue(resource, "name") |
||||
recordType, hasType := getStringValue(resource, "type") |
||||
//recordName, hasName := getStringValue(resource, "type")
|
||||
if !hasName { |
||||
return false, fmt.Errorf("gandi_livedns_record %s (from terraform planned_values) is missing string value \"name\" ", resource.Address) |
||||
} |
||||
if !hasType { |
||||
return false, fmt.Errorf("gandi_livedns_record %s (from terraform planned_values) is missing string value \"type\" ", resource.Address) |
||||
} |
||||
|
||||
recordId := fmt.Sprintf("%s/%s/%s", domainName, recordName, recordType) |
||||
|
||||
_, gandiAlreadyHasRecord := recordsFromAPIById[recordId] |
||||
|
||||
if gandiAlreadyHasRecord { |
||||
fmt.Println("---------------------------------------") |
||||
fmt.Printf( |
||||
"Terraform plans to add the DNS record %s %s to gandi, but gandi already has a record like that \n", |
||||
recordType, domainName, |
||||
) |
||||
exitCode := removeAndImportResource(terraformDirectory, resource.Address, recordId) |
||||
|
||||
if exitCode != 0 { |
||||
return false, errors.New("terraform import returned a non-zero exit code") |
||||
} |
||||
importedAnyRecords = true |
||||
|
||||
fmt.Println("---------------------------------------") |
||||
} else { |
||||
altType := "" |
||||
if recordType == "CNAME" { |
||||
altType = "A" |
||||
} |
||||
if recordType == "A" { |
||||
altType = "CNAME" |
||||
} |
||||
altRecordId := fmt.Sprintf("%s/%s/%s", domainName, recordName, altType) |
||||
|
||||
_, gandiHasAltRecord := recordsFromAPIById[altRecordId] |
||||
if gandiHasAltRecord { |
||||
|
||||
fmt.Printf( |
||||
"Terraform plans to add the DNS record [%s %s] to gandi, but gandi has a conflicting record [%s %s]. Deleting the conflicting record...\n", |
||||
recordType, domainName, altType, domainName, |
||||
) |
||||
|
||||
gandiURL := fmt.Sprintf( |
||||
"%s/%s/%s/%s/%s/%s", |
||||
configuration.GANDI_API_URL, "v5/livedns/domains", domainName, "records", recordName, altType, |
||||
) |
||||
request, err := http.NewRequest("DELETE", gandiURL, nil) |
||||
request.Header.Add("Authorization", fmt.Sprintf("Apikey %s", gandiCredential.Password)) |
||||
response, err := httpClient.Do(request) |
||||
|
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if response.StatusCode != 200 { |
||||
return false, fmt.Errorf("HTTP %d when calling DELETE %s ", gandiURL, response.StatusCode) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
return importedAnyRecords, nil |
||||
var gandiCredential *configuration.Credential = nil |
||||
for _, cred := range config.Credentials { |
||||
if cred.Type == configuration.GANDI { |
||||
gandiCredential = &cred |
||||
break |
||||
} |
||||
} |
||||
|
||||
createLiveDNSRecords := filterResourceChanges( |
||||
tfShow, |
||||
ResourceChangeFilter{ResourceType: "gandi_livedns_record", Action: "create", NotAction: "delete"}, |
||||
) |
||||
|
||||
domainName, hasDomainName := config.Terraform.Variables["domain_name"] |
||||
|
||||
if gandiCredential == nil || len(createLiveDNSRecords) == 0 || !hasDomainName { |
||||
return false, nil |
||||
} |
||||
|
||||
// Get the list of DNS records from the Gandi API
|
||||
httpClient := http.Client{} |
||||
gandiURL := fmt.Sprintf( |
||||
"%s/%s/%s/%s", |
||||
configuration.GANDI_API_URL, "v5/livedns/domains", domainName, "records", |
||||
) |
||||
request, err := http.NewRequest("GET", gandiURL, nil) |
||||
|
||||
request.Header.Add("Authorization", fmt.Sprintf("Apikey %s", gandiCredential.Password)) |
||||
response, err := httpClient.Do(request) |
||||
|
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if response.StatusCode != 200 { |
||||
return false, fmt.Errorf("HTTP %d when calling %s ", gandiURL, response.StatusCode) |
||||
} |
||||
bytes, err := ioutil.ReadAll(response.Body) |
||||
if err != nil { |
||||
return false, errors.Wrapf(err, "HTTP read error when calling %s ", gandiURL) |
||||
} |
||||
var recordsFromAPI []gandiLiveDNSRecord |
||||
err = json.Unmarshal(bytes, &recordsFromAPI) |
||||
if err != nil { |
||||
return false, errors.Wrapf(err, "JSON parse error when calling %s ", gandiURL) |
||||
} |
||||
|
||||
recordsFromAPIById := map[string]gandiLiveDNSRecord{} |
||||
|
||||
for _, record := range recordsFromAPI { |
||||
recordsFromAPIById[fmt.Sprintf("%s/%s/%s", domainName, record.Name, record.Type)] = record |
||||
} |
||||
|
||||
importedAnyRecords := false |
||||
|
||||
// for each record terraform plans to create, check if it already exists in Gandi
|
||||
for _, resource := range createLiveDNSRecords { |
||||
recordName, hasName := getStringValue(resource, "name") |
||||
recordType, hasType := getStringValue(resource, "type") |
||||
//recordName, hasName := getStringValue(resource, "type")
|
||||
if !hasName { |
||||
return false, fmt.Errorf("gandi_livedns_record %s (from terraform planned_values) is missing string value \"name\" ", resource.Address) |
||||
} |
||||
if !hasType { |
||||
return false, fmt.Errorf("gandi_livedns_record %s (from terraform planned_values) is missing string value \"type\" ", resource.Address) |
||||
} |
||||
|
||||
recordId := fmt.Sprintf("%s/%s/%s", domainName, recordName, recordType) |
||||
|
||||
_, gandiAlreadyHasRecord := recordsFromAPIById[recordId] |
||||
|
||||
if gandiAlreadyHasRecord { |
||||
fmt.Println("---------------------------------------") |
||||
fmt.Printf( |
||||
"Terraform plans to add the DNS record %s %s to gandi, but gandi already has a record like that \n", |
||||
recordType, domainName, |
||||
) |
||||
exitCode := removeAndImportResource(terraformDirectory, resource.Address, recordId) |
||||
|
||||
if exitCode != 0 { |
||||
return false, errors.New("terraform import returned a non-zero exit code") |
||||
} |
||||
importedAnyRecords = true |
||||
|
||||
fmt.Println("---------------------------------------") |
||||
} else { |
||||
altType := "" |
||||
if recordType == "CNAME" { |
||||
altType = "A" |
||||
} |
||||
if recordType == "A" { |
||||
altType = "CNAME" |
||||
} |
||||
altRecordId := fmt.Sprintf("%s/%s/%s", domainName, recordName, altType) |
||||
|
||||
_, gandiHasAltRecord := recordsFromAPIById[altRecordId] |
||||
if gandiHasAltRecord { |
||||
|
||||
fmt.Printf( |
||||
"Terraform plans to add the DNS record [%s %s] to gandi, but gandi has a conflicting record [%s %s]. Deleting the conflicting record...\n", |
||||
recordType, domainName, altType, domainName, |
||||
) |
||||
|
||||
gandiURL := fmt.Sprintf( |
||||
"%s/%s/%s/%s/%s/%s", |
||||
configuration.GANDI_API_URL, "v5/livedns/domains", domainName, "records", recordName, altType, |
||||
) |
||||
request, err := http.NewRequest("DELETE", gandiURL, nil) |
||||
request.Header.Add("Authorization", fmt.Sprintf("Apikey %s", gandiCredential.Password)) |
||||
response, err := httpClient.Do(request) |
||||
|
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if response.StatusCode != 200 { |
||||
return false, fmt.Errorf("HTTP %d when calling DELETE %s ", gandiURL, response.StatusCode) |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
return importedAnyRecords, nil |
||||
} |
||||
|
@ -1,81 +1,81 @@
|
||||
package automation |
||||
|
||||
import ( |
||||
"fmt" |
||||
"fmt" |
||||
) |
||||
|
||||
func getStringValue(change *TerraformShowResourceChange, key string) (string, bool) { |
||||
interfaceValue, has := change.Values[key] |
||||
if !has { |
||||
return "", false |
||||
} |
||||
switch stringValue := interfaceValue.(type) { |
||||
case string: |
||||
return stringValue, true |
||||
default: |
||||
return "", false |
||||
} |
||||
interfaceValue, has := change.Values[key] |
||||
if !has { |
||||
return "", false |
||||
} |
||||
switch stringValue := interfaceValue.(type) { |
||||
case string: |
||||
return stringValue, true |
||||
default: |
||||
return "", false |
||||
} |
||||
} |
||||
|
||||
func getStringSliceValue(change *TerraformShowResourceChange, key string) ([]string, bool) { |
||||
interfaceValue, has := change.Values[key] |
||||
if !has { |
||||
return []string{}, false |
||||
} |
||||
switch stringSliceValue := interfaceValue.(type) { |
||||
case []string: |
||||
return stringSliceValue, true |
||||
default: |
||||
return []string{}, false |
||||
} |
||||
interfaceValue, has := change.Values[key] |
||||
if !has { |
||||
return []string{}, false |
||||
} |
||||
switch stringSliceValue := interfaceValue.(type) { |
||||
case []string: |
||||
return stringSliceValue, true |
||||
default: |
||||
return []string{}, false |
||||
} |
||||
} |
||||
|
||||
func removeAndImportResource(terraformDirectory, address, id string) int { |
||||
|
||||
// fullyQualifiedResourcePath := fmt.Sprintf("%s[%d]", tfResourcePath, index)
|
||||
fmt.Printf( |
||||
"to fix this, I will run: \n(%s) $ terraform state rm %s\n(%s) $ terraform import %s %s\n\n", |
||||
terraformDirectory, address, terraformDirectory, address, id, |
||||
) |
||||
exitCode, stdout, stderr, _ := shellExec(terraformDirectory, "terraform", "state", "rm", address) |
||||
fmt.Printf( |
||||
"exitCode: %d\n\nstdout:\n%s\n\nstderr:\n%s\n\n", |
||||
exitCode, stdout, stderr, |
||||
) |
||||
exitCode, stdout, stderr, err := shellExec(terraformDirectory, "terraform", "import", address, id) |
||||
fmt.Printf( |
||||
"exitCode: %d\n\nstdout:\n%s\n\nstderr:\n%s\n\n", |
||||
exitCode, stdout, stderr, |
||||
) |
||||
if err != nil { |
||||
fmt.Println("shellExec failed! aborting.") |
||||
return exitCode |
||||
} |
||||
return exitCode |
||||
// fullyQualifiedResourcePath := fmt.Sprintf("%s[%d]", tfResourcePath, index)
|
||||
fmt.Printf( |
||||
"to fix this, I will run: \n(%s) $ terraform state rm %s\n(%s) $ terraform import %s %s\n\n", |
||||
terraformDirectory, address, terraformDirectory, address, id, |
||||
) |
||||
exitCode, stdout, stderr, _ := shellExec(terraformDirectory, "terraform", "state", "rm", address) |
||||
fmt.Printf( |
||||
"exitCode: %d\n\nstdout:\n%s\n\nstderr:\n%s\n\n", |
||||
exitCode, stdout, stderr, |
||||
) |
||||
exitCode, stdout, stderr, err := shellExec(terraformDirectory, "terraform", "import", address, id) |
||||
fmt.Printf( |
||||
"exitCode: %d\n\nstdout:\n%s\n\nstderr:\n%s\n\n", |
||||
exitCode, stdout, stderr, |
||||
) |
||||
if err != nil { |
||||
fmt.Println("shellExec failed! aborting.") |
||||
return exitCode |
||||
} |
||||
return exitCode |
||||
} |
||||
|
||||
func filterResourceChanges(tfShow *TerraformShow, filter ResourceChangeFilter) []*TerraformShowResourceChange { |
||||
toReturn := []*TerraformShowResourceChange{} |
||||
toReturn := []*TerraformShowResourceChange{} |
||||
|
||||
for _, resource := range tfShow.ResourceChanges { |
||||
hasAction := filter.Action == "" |
||||
hasNotAction := false |
||||
for _, action := range resource.Change.Actions { |
||||
if action == filter.Action { |
||||
hasAction = true |
||||
} |
||||
if action == filter.NotAction { |
||||
hasNotAction = true |
||||
} |
||||
} |
||||
// fmt.Printf(
|
||||
// "resource.Type: %s == filter.ResourceType: %s, hasAction %t, !hasNotAction %t \n",
|
||||
// resource.Type, filter.ResourceType, hasAction, !hasNotAction,
|
||||
// )
|
||||
if resource.Type == filter.ResourceType && hasAction && !hasNotAction { |
||||
toReturn = append(toReturn, resource) |
||||
} |
||||
for _, resource := range tfShow.ResourceChanges { |
||||
hasAction := filter.Action == "" |
||||
hasNotAction := false |
||||
for _, action := range resource.Change.Actions { |
||||
if action == filter.Action { |
||||
hasAction = true |
||||
} |
||||
if action == filter.NotAction { |
||||
hasNotAction = true |
||||
} |
||||
} |
||||
// fmt.Printf(
|
||||
// "resource.Type: %s == filter.ResourceType: %s, hasAction %t, !hasNotAction %t \n",
|
||||
// resource.Type, filter.ResourceType, hasAction, !hasNotAction,
|
||||
// )
|
||||
if resource.Type == filter.ResourceType && hasAction && !hasNotAction { |
||||
toReturn = append(toReturn, resource) |
||||
} |
||||
|
||||
} |
||||
return toReturn |
||||
} |
||||
return toReturn |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff