Skip to content

Commit

Permalink
Merge pull request #176 from ans-group/lb-tf-deploy-wrapper
Browse files Browse the repository at this point in the history
Provide wrapper for Terraform to handle loadbalancer deployments
  • Loading branch information
Xiol authored Feb 13, 2024
2 parents fe295ad + f7e1117 commit 8456f75
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
FROM golang:1.18-alpine3.16 AS builder
FROM golang:1.21-alpine AS builder
RUN apk --no-cache add git
COPY . /build/
WORKDIR /build
ENV CGO_ENABLED 0
RUN go build -o ans -ldflags "-s -X 'main.VERSION=$(git describe --tags)' -X 'main.BUILDDATE=$(date +'%Y-%m-%dT%H:%M:%S')'"

FROM alpine:3.16
FROM alpine:3
RUN apk --no-cache add ca-certificates bash bash-completion
COPY --from=builder /build/ans /bin/ans
RUN echo "source /usr/share/bash-completion/bash_completion" >> ~/.bashrc
Expand Down
1 change: 1 addition & 0 deletions cmd/loadbalancer/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func LoadBalancerRootCmd(f factory.ClientFactory, fs afero.Fs) *cobra.Command {
cmd.AddCommand(loadbalancerDeploymentRootCmd(f))
cmd.AddCommand(loadbalancerListenerRootCmd(f, fs))
cmd.AddCommand(loadbalancerTargetGroupRootCmd(f))
cmd.AddCommand(loadbalancerTerraformCmd(f))

return cmd
}
Expand Down
134 changes: 134 additions & 0 deletions cmd/loadbalancer/loadbalancer_terraform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package loadbalancer

import (
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"slices"
"strings"

"github.com/ans-group/cli/internal/pkg/factory"
"github.com/ans-group/sdk-go/pkg/service/loadbalancer"
"github.com/spf13/cobra"
)

const clusterIDsOutputKey string = "loadbalancer_cluster_ids"

func loadbalancerTerraformCmd(f factory.ClientFactory) *cobra.Command {
cmd := &cobra.Command{
Use: "terraform",
Short: "Terraform wrapper for handling deployments",
Long: "Wraps Terraform to automatically deploy your changes after a successful apply. You must " +
fmt.Sprintf("ensure you have configured a Terraform output called '%s' which ", clusterIDsOutputKey) +
"contains the IDs of your loadbalancer clusters. After applying your Terraform configuration, " +
"we'll deploy your staged changes to the loadbalancer.",
Example: "ans loadbalancer terraform apply",
DisableFlagParsing: true,
RunE: loadbalancerCobraRunEFunc(f, loadbalancerTerraform),
}

return cmd
}

func loadbalancerTerraform(service loadbalancer.LoadBalancerService, cmd *cobra.Command, args []string) error {
// Because we disable flag parsing, we'll need to handle --help ourselves
if slices.Contains(args, "-h") || slices.Contains(args, "--help") || len(args) == 0 {
return cmd.Help()
}

binPath, err := findTerraformBinary()
if err != nil {
return err
}

if err := runTerraform(binPath, args); err != nil {
return err
}

// If we're not applying the configuration then exit now
if !slices.Contains(args, "apply") {
return nil
}

fmt.Printf("\nTerraform run complete, deploying the configuration to the loadbalancer...\n")

clusterIDs, err := getClusterIDs(binPath)
if err != nil {
return err
}

fmt.Printf("Deploying cluster(s): %s\n\n",
strings.Trim(strings.Join(strings.Fields(fmt.Sprint(clusterIDs)), ", "), "[]"))

haveErrors := false
for _, clusterID := range clusterIDs {
fmt.Printf("Deploying %d... ", clusterID)
err = service.DeployCluster(clusterID)
if err != nil {
haveErrors = true
fmt.Printf("failed to deploy cluster: %d: %s\n", clusterID, err)
} else {
fmt.Printf("ok\n\n")
}
}

if haveErrors {
return fmt.Errorf("\nans: some clusters failed to deploy, please see above output")
}

return nil
}

func findTerraformBinary() (string, error) {
path, err := exec.LookPath("terraform")
if err != nil {
path, err = exec.LookPath("tofu")
if err != nil {
return "", fmt.Errorf("ans: no terraform or tofu binaries found in path")
}
}
return path, nil
}

func runTerraform(binPath string, args []string) error {
tfCmd := exec.Command(binPath, args...)
tfCmd.Stdout = os.Stdout
tfCmd.Stderr = os.Stderr
tfCmd.Stdin = os.Stdin

if err := tfCmd.Run(); err != nil {
var exitError *exec.ExitError
if errors.As(err, &exitError) {
return fmt.Errorf("ans: %s exited with %d, aborting deployment", binPath, exitError.ExitCode())
}
return fmt.Errorf("ans: error running %s: %s", binPath, err)
}

return nil
}

func getClusterIDs(binPath string) ([]int, error) {
// Get the cluster ID from Terraform
output, err := exec.Command(binPath, "output", "-json", clusterIDsOutputKey).CombinedOutput()
if err != nil {
fmt.Printf("%s\n", string(output))
return nil, fmt.Errorf("ans: deployment failed: failed to get %s output from terraform, cannot deploy: %s",
clusterIDsOutputKey, err)
}

var clusterIDs []int
err = json.Unmarshal(output, &clusterIDs)
if err != nil {
return nil, fmt.Errorf("ans: deployment failed: failed to unmarshal Terraform output from key '%s': %s",
clusterIDsOutputKey, err)
}

if len(clusterIDs) == 0 {
return nil, fmt.Errorf("ans: deployment failed: no cluster IDs found in Terraform output key '%s'",
clusterIDsOutputKey)
}

return clusterIDs, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/ans-group/cli

go 1.20
go 1.21

require (
github.com/ans-group/sdk-go v1.17.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
Expand Down Expand Up @@ -117,6 +118,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo=
github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
Expand Down Expand Up @@ -158,9 +160,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
Expand All @@ -177,6 +181,7 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
Expand All @@ -194,6 +199,7 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
Expand Down Expand Up @@ -558,6 +564,7 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
Expand Down

0 comments on commit 8456f75

Please sign in to comment.