server.garden privileged automation agent (mirror of https://git.sequentialread.com/forest/rootsystem)
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
4.7 KiB
165 lines
4.7 KiB
package main |
|
|
|
import ( |
|
"encoding/json" |
|
"fmt" |
|
"log" |
|
"net/http" |
|
|
|
"git.sequentialread.com/forest/rootsystem/automation" |
|
"git.sequentialread.com/forest/rootsystem/configuration" |
|
"git.sequentialread.com/forest/rootsystem/objectStorage" |
|
) |
|
|
|
type applicationState struct { |
|
workingDirectory string |
|
storage objectStorage.ObjectStorager |
|
} |
|
|
|
var global applicationState |
|
|
|
// func main() { |
|
// config, workingDirectory, err := configuration.LoadConfiguration() |
|
// if err != nil { |
|
// panic(errors.Wrap(err, "rootsystem can't start because loadConfiguration() returned")) |
|
// } |
|
// global.workingDirectory = workingDirectory |
|
|
|
// storage, err := objectStorage.InitializeObjectStorage(config, true) |
|
// if err != nil { |
|
// panic(errors.Wrap(err, "rootsystem can't start because failed to initialize object storage")) |
|
// } |
|
// global.storage = storage |
|
|
|
// go terraformStateServer() |
|
|
|
// // This creates an access key that the gateway cloud instance can use to upload its SSH public key |
|
// // to our object storage. the host-key-poller will download this SSH host public key and add it to our known_hosts |
|
// // so that we can SSH to the gateway instance securely |
|
// hostKeysAccessSpec := objectStorage.ObjectStorageKey{ |
|
// Name: "rootsystem-known-hosts", |
|
// PathPrefix: "rootsystem/known-hosts", |
|
// Read: true, |
|
// Write: true, |
|
// Delete: false, |
|
// List: false, |
|
// } |
|
// knownHostsCredentials, err := global.storage.CreateAccessKeyIfNotExists(hostKeysAccessSpec) |
|
// if err != nil { |
|
// panic(err) |
|
// } |
|
|
|
// // BuildTLSCertsForThreshold fills in the CAs, Keys, and Certificates in the Threshold ansible roles. |
|
// // So when terraform invokes ansible to install threshold client/server, it will install working |
|
// // certificates and keys |
|
// err = pki.BuildTLSCertsForThreshold( |
|
// global.workingDirectory, |
|
// config.Terraform.Variables["domain_name"], |
|
// config.Host.Name, |
|
// global.storage, |
|
// ) |
|
// if err != nil { |
|
// panic(err) |
|
// } |
|
|
|
// // First, run the terraform build for the GLOBAL components, meaning the components |
|
// // that exist in the cloud, independent of how many server.garden nodes are being used. |
|
// outputVariables, err := terraformBuild( |
|
// config, |
|
// automation.TerraformConfiguration{ |
|
// TargetedModules: config.Terraform.GlobalModules, |
|
// TerraformProject: configuration.GLOBAL_TERRAFORM_PROJECT, |
|
// HostKeysObjectStorageCredentials: knownHostsCredentials, |
|
// }, |
|
// ) |
|
// if err != nil { |
|
// panic(err) |
|
// } |
|
|
|
// os.Exit(0) |
|
|
|
// // Next, we run a separate LOCAL terraform build which is specific to THIS server.garden node, |
|
// // this build will be responsible for installing software on this node & registering this node with the |
|
// // cloud resources |
|
// _, err = terraformBuild( |
|
// config, |
|
// automation.TerraformConfiguration{ |
|
// TargetedModules: config.Terraform.LocalModules, |
|
// TerraformProject: fmt.Sprintf("%s-%s", configuration.LOCAL_TERRAFORM_PROJECT, config.Host.Name), |
|
// RemoteState: configuration.GLOBAL_TERRAFORM_PROJECT, |
|
// RemoteStateVariables: outputVariables, |
|
// }, |
|
// ) |
|
// if err != nil { |
|
// panic(err) |
|
// } |
|
|
|
// a := make(chan bool) |
|
|
|
// <-a |
|
|
|
// } |
|
|
|
func terraformBuild( |
|
config *configuration.Configuration, |
|
terraformConfig automation.TerraformConfiguration, |
|
) ([]string, error) { |
|
|
|
outputVariables, err := automation.WriteTerraformCodeForTargetedModules( |
|
config, |
|
global.workingDirectory, |
|
terraformConfig, |
|
) |
|
if err != nil { |
|
return []string{}, err |
|
} |
|
|
|
fmt.Println("WriteTerraformCodeForTargetedModules done") |
|
|
|
svg, statusChannel, err := automation.TerraformPlanAndApply(config, global.workingDirectory, terraformConfig.TerraformProject) |
|
if err != nil { |
|
return []string{}, err |
|
} |
|
|
|
fmt.Println("TerraformPlanAndApply done") |
|
|
|
err = global.storage.Put("rootsystem/terraform/diagram.svg", []byte(svg)) |
|
if err != nil { |
|
return []string{}, err |
|
} |
|
|
|
var terraformPlanAndApplyError error = nil |
|
|
|
for status := range statusChannel { |
|
json, err := json.MarshalIndent(status, "", " ") |
|
if err != nil { |
|
return []string{}, err |
|
} |
|
log.Println(status.Log) |
|
err = global.storage.Put("rootsystem/terraform/status.json", []byte(json)) |
|
if err != nil { |
|
return []string{}, err |
|
} |
|
if status.Complete { |
|
terraformPlanAndApplyError = status.Error |
|
} |
|
} |
|
|
|
if terraformPlanAndApplyError != nil { |
|
return outputVariables, terraformPlanAndApplyError |
|
} |
|
|
|
return outputVariables, nil |
|
} |
|
|
|
func terraformStateServer() error { |
|
|
|
// Make sure to only listen on localhost. |
|
// TODO change this to HTTPS or unix socket |
|
server := http.Server{ |
|
Addr: fmt.Sprintf("127.0.0.1:%d", configuration.TERRAFORM_STATE_SERVER_PORT_NUMBER), |
|
Handler: terraformStateHandler{}, |
|
} |
|
|
|
return server.ListenAndServe() |
|
}
|
|
|