Browse Source

refactoring around terraform autofixes and got gandi DNS working

master
forest 2 years ago
parent
commit
f4e14b993c
  1. 3
      .gitignore
  2. 8
      ansible-roles/threshold-client-config/tasks/main.yml
  3. 2
      ansible-roles/threshold-register-client-with-server/tasks/main.yml
  4. 6
      ansible-roles/threshold-server-config/tasks/main.yml
  5. 39
      ansible-roles/threshold/tasks/main.yml
  6. 133
      automation/patchDigitalOcean.go
  7. 148
      automation/patchGandi.go
  8. 81
      automation/patchHelpers.go
  9. 120
      automation/terraformActions.go
  10. 2
      automation/terraformCodeGeneration.go
  11. 1
      configuration/configuration.go
  12. 7
      terraform-modules/dns-gandi/main.tf
  13. 3
      terraform-modules/gateway-dns/main.tf

3
.gitignore vendored

@ -22,4 +22,5 @@ ansible-playbook-wrapper
terraform_apply_log.txt
host-key-poller/host-key-poller
build
localbuild.sh
localbuild.sh
tfshow.json

8
ansible-roles/threshold-client-config/tasks/main.yml

@ -1,7 +1,7 @@
- name: install threshold config file
template:
src: config.j2
dest: /root/threshold/config.json
dest: /opt/threshold/config.json
owner: threshold
group: threshold
mode: '0600'
@ -9,7 +9,7 @@
- name: install CA cert used to sign the server's key
copy:
src: '{{ domain }}_CA.crt'
dest: '/root/threshold/{{ domain }}_CA.crt'
dest: '/opt/threshold/{{ domain }}_CA.crt'
owner: threshold
group: threshold
mode: '0600'
@ -17,7 +17,7 @@
- name: install threshold client TLS certificate
copy:
src: '{{ clientId }}@{{ domain }}.crt'
dest: '/root/threshold/{{ clientId }}@{{ domain }}.crt'
dest: '/opt/threshold/{{ clientId }}@{{ domain }}.crt'
owner: threshold
group: threshold
mode: '0600'
@ -25,7 +25,7 @@
- name: install threshold client TLS key
copy:
src: '{{ clientId }}@{{ domain }}.key'
dest: '/root/threshold/{{ clientId }}@{{ domain }}.key'
dest: '/opt/threshold/{{ clientId }}@{{ domain }}.key'
owner: threshold
group: threshold
mode: '0600'

2
ansible-roles/threshold-register-client-with-server/tasks/main.yml

@ -1,7 +1,7 @@
- name: install threshold CA
copy:
src: '{{ clientId }}_CA.crt'
dest: '/root/threshold/{{ clientId }}_CA.crt'
dest: '/opt/threshold/{{ clientId }}_CA.crt'
owner: threshold
group: threshold
mode: '0600'

6
ansible-roles/threshold-server-config/tasks/main.yml

@ -2,7 +2,7 @@
- name: install threshold config file
template:
src: config.j2
dest: /root/threshold/config.json
dest: /opt/threshold/config.json
owner: threshold
group: threshold
mode: '0600'
@ -10,7 +10,7 @@
- name: install threshold server TLS certificate
copy:
src: '{{ domain }}.crt'
dest: '/root/threshold/{{ domain }}.crt'
dest: '/opt/threshold/{{ domain }}.crt'
owner: threshold
group: threshold
mode: '0600'
@ -18,7 +18,7 @@
- name: install threshold server TLS key
copy:
src: '{{ domain }}.key'
dest: '/root/threshold/{{ domain }}.key'
dest: '/opt/threshold/{{ domain }}.key'
owner: threshold
group: threshold
mode: '0600'

39
ansible-roles/threshold/tasks/main.yml

@ -1,11 +1,27 @@
- name: Ensure threshold user group exists
group:
name: threshold
state: present
- name: Ensure threshold user exists
user:
name: threshold
state: present
group: threshold
- name: ensure threshold folder exists
file:
path: /root/threshold
path: /opt/threshold
state: directory
owner: threshold
group: threshold
mode: '0600'
- name: checksum the Threshold binary
stat:
path: /root/threshold/threshold
path: /opt/threshold/threshold
checksum_algorithm: sha256
register: threshold_binary
- name: log the checksum
@ -23,21 +39,18 @@
unarchive:
remote_src: yes
src: '/tmp/threshold-{{ arch }}.tar.gz'
dest: /root/threshold
dest: /opt/threshold
when: threshold_binary.stat.checksum is not defined or threshold_binary.stat.checksum != bin_sha256[arch]
- name: set owner, group and permissions on threshold binary
file:
path: /opt/threshold/threshold
owner: threshold
group: threshold
mode: '0700'
- name: clean up threshold tar.gz file
file:
path: '/tmp/threshold-{{ arch }}.tar.gz'
state: absent
- name: Ensure threshold user group exists
group:
name: threshold
state: present
- name: Ensure threshold user exists
user:
name: threshold
state: present
group: threshold

133
automation/patchDigitalOcean.go

@ -5,7 +5,6 @@ import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
@ -24,11 +23,10 @@ type digitalOceanSSHKey struct {
Pame string `json:"name"`
}
func handleDigitalOceanSSHKeyAlreadyExistsBug(
func handleDigitalOceanSSHKeyAlreadyExists(
config *configuration.Configuration,
workingDirectory string,
terraformDirectory string,
status *SimplifiedTerraformStatus,
tfShow *TerraformShow,
) (bool, error) {
@ -40,67 +38,22 @@ func handleDigitalOceanSSHKeyAlreadyExistsBug(
}
}
if digitaloceanCredential == nil {
return false, nil
}
// Find all changed digitalocean_ssh_key resources in terraform plan
changedSSHKeyResourcePaths := []string{}
for _, module := range status.Modules {
for _, resource := range module.Resources {
if resource.ResourceType == "digitalocean_ssh_key" && resource.Plan != "none" {
changedSSHKeyResourcePaths = append(
changedSSHKeyResourcePaths,
//module.ssh-keys-digitalocean.digitalocean_ssh_key.default
fmt.Sprintf("module.%s.%s", module.DisplayName, resource.DisplayName),
)
}
}
}
// fmt.Println(changedSSHKeyResourcePaths)
if len(changedSSHKeyResourcePaths) == 0 {
return false, nil
}
createSSHKeys := filterResourceChanges(
tfShow,
ResourceChangeFilter{ResourceType: "digitalocean_ssh_key", Action: "create", NotAction: "delete"},
)
createdIndexes := map[int]string{}
// use the tfShow data to identify the index of each sshPublicKey that will be created
for _, resourcePath := range changedSSHKeyResourcePaths {
for _, resourceChange := range tfShow.ResourceChanges {
hasCreate := false
for _, action := range resourceChange.Change.Actions {
if action == "create" {
hasCreate = true
}
}
if hasCreate && strings.HasPrefix(resourceChange.Address, resourcePath) {
indexMatches := regexp.MustCompile(`\[([0-9]+)\]$`).FindStringSubmatch(resourceChange.Address)
if len(indexMatches) > 1 {
index, err := strconv.Atoi(indexMatches[1])
if err == nil {
createdIndexes[index] = resourcePath
}
}
}
}
}
// fmt.Println(createdIndexes)
if len(createdIndexes) == 0 {
if digitaloceanCredential == nil || len(createSSHKeys) == 0 {
return false, nil
}
// Get the ssh public keys from disk
sshPublicKeys, err := getSSHPublicKeys(workingDirectory)
if err != nil {
return false, err
}
// 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{}
@ -126,54 +79,34 @@ func handleDigitalOceanSSHKeyAlreadyExistsBug(
digitalOceanIdByKeyContent := map[string]int{}
for _, sshKey := range responseObject.SSHKeys {
digitalOceanIdByKeyContent[sshKey.PublicKey] = sshKey.Id
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 index, tfResourcePath := range createdIndexes {
if index < len(sshPublicKeys) {
localKey := sshPublicKeys[index]
keyPath := localKey[0]
keyName := localKey[1]
localKeyBytes, err := ioutil.ReadFile(keyPath)
if err != nil {
return false, err
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
digitalOceanId, has := digitalOceanIdByKeyContent[strings.TrimSpace(string(localKeyBytes))]
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",
keyName,
)
fullyQualifiedResourcePath := fmt.Sprintf("%s[%d]", tfResourcePath, index)
fmt.Printf(
"to fix this, I will run: \n(%s) $ terraform import %s %d\n\n",
terraformDirectory, fullyQualifiedResourcePath, digitalOceanId,
)
exitCode, stdout, stderr, err := shellExec(terraformDirectory, "terraform", "import", fullyQualifiedResourcePath, strconv.Itoa(digitalOceanId))
if err != nil {
fmt.Println("shellExec failed! aborting.")
return false, err
}
fmt.Printf(
"exitCode: %d\n\nstdout:\n%s\n\nstderr:\n%s\n\n",
exitCode, stdout, stderr,
)
fmt.Println("---------------------------------------")
if exitCode != 0 {
return false, errors.New("terraform import returned a non-zero exit code")
}
importedAnySSHKeys = true
}
} else {
return false, fmt.Errorf("ssh key index %d (from terraform plan) is out of range", index)
fmt.Println("---------------------------------------")
}
}
return importedAnySSHKeys, nil

148
automation/patchGandi.go

@ -0,0 +1,148 @@
package automation
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
errors "git.sequentialread.com/forest/pkg-errors"
"git.sequentialread.com/forest/rootsystem/configuration"
)
type gandiLiveDNSRecord struct {
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,
) (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
}

81
automation/patchHelpers.go

@ -0,0 +1,81 @@
package automation
import (
"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
}
}
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
}
}
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
}
func filterResourceChanges(tfShow *TerraformShow, filter ResourceChangeFilter) []*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)
}
}
return toReturn
}

120
automation/terraformActions.go

@ -19,9 +19,17 @@ import (
)
type TerraformShow struct {
//PlannedValues map[string]map[string][]TerraformShowModule `json:"planned_values"`
Configuraton TerraformShowConfiguration `json:"configuration"`
ResourceChanges []TerraformShowResourceChange `json:"resource_changes"`
PlannedValues TerraformShowPlannedValues `json:"planned_values"`
Configuraton TerraformShowConfiguration `json:"configuration"`
ResourceChanges []*TerraformShowResourceChange `json:"resource_changes"`
}
type TerraformShowPlannedValues struct {
RootModule *TerraformShowPlannedValuesRoot `json:"root_module"`
}
type TerraformShowPlannedValuesRoot struct {
ChildModules []*TerraformShowModule `json:"child_modules"`
}
type TerraformShowConfiguration struct {
@ -33,16 +41,26 @@ type TerraformShowRootModule struct {
}
type TerraformShowModule struct {
Resources []TerraformShowResource `json:"resources"`
Resources []*TerraformShowResource `json:"resources"`
Address string `json:"address"`
}
type TerraformShowResource struct {
Address string `json:"address"`
Type string `json:"type"`
Address string `json:"address"`
Type string `json:"type"`
Name string `json:"name"`
Index int `json:"index"`
Values map[string]interface{} `json:"values"`
}
type ResourceChangeFilter struct {
ResourceType string
Action string
NotAction string
}
type TerraformShowResourceChange struct {
Address string `json:"address"`
TerraformShowResource
ModuleAddress string `json:"module_address"`
Change TerraformShowChangeSpec `json:"change"`
}
@ -132,7 +150,7 @@ func TerraformPlanAndApply(
}
// Convenience function so we can plan multiple times if needed
doPlan := func() (*SimplifiedTerraformStatus, *TerraformShow, string, error) {
doPlan := func(terraformDirectory string) (*SimplifiedTerraformStatus, *TerraformShow, string, error) {
exitCode, planStdout, planStderr, err := shellExec(terraformDirectory, "terraform", "plan", "-out", configuration.TERRAFORM_PLAN_FILE_NAME)
err = errorFromShellExecResult("terraform plan", exitCode, planStdout, planStderr, err)
@ -160,15 +178,37 @@ func TerraformPlanAndApply(
// }
// log.Println(string(json))
// Copy some values over from PlannedValues to ResourceChanges for convenience access later
changedResources := map[string]*TerraformShowResource{}
for _, module := range tfShow.PlannedValues.RootModule.ChildModules {
for _, resource := range module.Resources {
changedResources[resource.Address] = resource
}
}
for _, changedResource := range tfShow.ResourceChanges {
resourceWithValues := changedResources[changedResource.Address]
changedResource.Index = resourceWithValues.Index
changedResource.Type = resourceWithValues.Type
changedResource.Name = resourceWithValues.Name
changedResource.Values = resourceWithValues.Values
}
simpleStatus, err := makeSimplifiedTerraformStatus(config, workingDirectory, tfShow)
if err != nil {
return nil, nil, "", errors.Wrap(err, "can't TerraformPlanAndApply because can't makeSimplifiedTerraformStatus")
}
// json, err := json.MarshalIndent(simpleStatus, "", " ")
// if err != nil {
// return nil, errors.Wrap(err, "can't GenerateTerraformPlan because can't json.Marshal the simpleStatus")
// }
// log.Println(string(json))
return &simpleStatus, &tfShow, string(planStdout), nil
}
simpleStatus, tfShow, planStdout, err := doPlan()
simpleStatus, tfShow, planStdout, err := doPlan(terraformDirectory)
if err != nil {
return "", nil, errors.Wrap(err, "can't TerraformPlanAndApply because can't doPlan()")
}
@ -178,41 +218,28 @@ func TerraformPlanAndApply(
// DigitalOcean
// TODO remove/import any orphaned server.garden tagged instances?
hasDigitalOcean := false
for _, cred := range config.Credentials {
if cred.Type == configuration.DIGITALOCEAN {
hasDigitalOcean = true
}
mod, err := handleDigitalOceanSSHKeyAlreadyExists(config, workingDirectory, terraformDirectory, tfShow)
if mod {
stateModified = true
}
if hasDigitalOcean {
stateModified, err = handleDigitalOceanSSHKeyAlreadyExistsBug(
config,
workingDirectory,
terraformDirectory,
simpleStatus,
tfShow,
)
if err != nil {
return "", nil, errors.Wrap(err, "can't TerraformPlanAndApply because can't handleKnownIssuesWithProviders")
}
// Gandi.net
//
mod, err = handleGandiLiveDNSRecordAlreadyExists(config, workingDirectory, terraformDirectory, tfShow)
if mod {
stateModified = true
}
// After we try to fix any known issues, we may have to plan again if the terraform state was changed
// for example, with terraform import or state commands.
if stateModified {
simpleStatus, tfShow, planStdout, err = doPlan()
simpleStatus, tfShow, planStdout, err = doPlan(terraformDirectory)
if err != nil {
return "", nil, errors.Wrap(err, "can't TerraformPlanAndApply because can't doPlan()")
}
}
// json, err := json.MarshalIndent(simpleStatus, "", " ")
// if err != nil {
// return nil, errors.Wrap(err, "can't GenerateTerraformPlan because can't json.Marshal the simpleStatus")
// }
// log.Println(string(json))
svg, err := makeSVGFromSimpleStatus(simpleStatus)
if err != nil {
return "", nil, errors.Wrap(err, "can't TerraformPlanAndApply because can't makeSVGFromSimpleStatus")
@ -220,7 +247,7 @@ func TerraformPlanAndApply(
for moduleName, module := range simpleStatus.Modules {
if module.IsAnsible {
err := linkAnsibleWrapperToModule(strings.TrimPrefix(moduleName, "module."), workingDirectory, terraformProject)
err := linkAnsibleWrapperToModule(strings.TrimPrefix(moduleName, "module."), workingDirectory, terraformProject, true)
if err != nil {
return "", nil, errors.Wrap(err, "can't TerraformPlanAndApply because can't linkAnsibleWrapperToModule")
}
@ -268,13 +295,14 @@ func TerraformPlanAndApply(
"\n",
)
go monitorTerraformApplyProgress(terraformDirectory, simpleStatus, process, logSoFar, logLinesChannel, toReturn)
go monitorTerraformApplyProgress(workingDirectory, terraformProject, simpleStatus, process, logSoFar, logLinesChannel, toReturn)
return svg, toReturn, nil
}
func monitorTerraformApplyProgress(
terraformDirectory string,
workingDirectory string,
terraformProject string,
simpleStatus *SimplifiedTerraformStatus,
applyProcess *exec.Cmd,
logSoFar string,
@ -285,6 +313,7 @@ func monitorTerraformApplyProgress(
//ansibleModules := map[string]bool
logLines := []string{}
terraformIsRunning := true
terraformDirectory := filepath.Join(workingDirectory, terraformProject)
// https://github.com/acarl005/stripansi/blob/master/stripansi.go
ansiEscapeRegex := regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")
@ -453,6 +482,16 @@ func monitorTerraformApplyProgress(
}
close(outputChannel)
// cleanUpSymlinksInTerraformProjects
for moduleName, module := range simpleStatus.Modules {
if module.IsAnsible {
linkAnsibleWrapperToModule(strings.TrimPrefix(moduleName, "module."), workingDirectory, terraformProject, false)
rolesFolder := filepath.Join(workingDirectory, configuration.TERRAFORM_MODULES, moduleName, "roles")
os.Remove(rolesFolder)
}
}
}
func makeSimplifiedTerraformStatus(
@ -786,17 +825,20 @@ func makeSVGFromSimpleStatus(simpleStatus *SimplifiedTerraformStatus) (string, e
return svgString, nil
}
func linkAnsibleWrapperToModule(moduleName, workingDirectory, terraformProject string) error {
func linkAnsibleWrapperToModule(moduleName, workingDirectory, terraformProject string, createLink bool) error {
ansibleDirectory := filepath.Join(workingDirectory, terraformProject, "modules", moduleName)
for _, toLink := range configuration.GET_ANSIBLE_WRAPPER_FILES() {
inModule := filepath.Join(ansibleDirectory, toLink)
inAnsibleWrapper := filepath.Join(workingDirectory, configuration.ANSIBLE_WRAPPER_PATH, toLink)
os.Remove(inModule)
err := os.Symlink(inAnsibleWrapper, inModule)
if err != nil {
return errors.Wrapf(err, "could not create symbolic link for ansible wrapper")
if createLink {
err := os.Symlink(inAnsibleWrapper, inModule)
if err != nil {
return errors.Wrapf(err, "could not create symbolic link for ansible wrapper")
}
}
}
return nil
}

2
automation/terraformCodeGeneration.go

@ -7,6 +7,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
errors "git.sequentialread.com/forest/pkg-errors"
@ -345,6 +346,7 @@ EOT
for _, provision := range fixedProvidedBy {
provisionStrings = append(provisionStrings, providedByToString(provision))
}
sort.Strings(provisionStrings)
argumentLines = append(argumentLines, fmt.Sprintf(
`
%s = [

1
configuration/configuration.go

@ -51,6 +51,7 @@ type Credential struct {
const GANDI = "Gandi"
const DIGITALOCEAN = "DigitalOcean"
const DIGITALOCEAN_API_URL = "https://api.digitalocean.com"
const GANDI_API_URL = "https://api.gandi.net"
const AMAZON_S3 = "AmazonS3"
const BACKBLAZE_B2 = "BackblazeB2"
const OBJECT_STORAGE_PASSPHRASE = "ObjectStoragePassphrase"

7
terraform-modules/dns-gandi/main.tf

@ -12,6 +12,13 @@ variable "dns_entry_list" {
}))
}
# data "gandi_domain" "domain" {
# }
# data "gandi_livedns_domain" "livedns_domain" {
# }
resource "gandi_livedns_record" "dns_entries" {
count = length(var.dns_entry_list)

3
terraform-modules/gateway-dns/main.tf

@ -1,11 +1,12 @@
variable "ingress_host_list" {
type = list(object({
ipv4 = string
ipv6 = string
arch = string
username = string
install_python = string
known_hosts_file_name = string
}))
}

Loading…
Cancel
Save