github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/e2e/consulacls/manage.go (about) 1 package consulacls 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "os/exec" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/stretchr/testify/require" 14 ) 15 16 // DefaultTFStateFile is the location of the TF state file, as created for the 17 // e2e test framework. This file is used to extract the TF serial number, which 18 // is used to determine whether the consul bootstrap process is necessary or has 19 // already taken place. 20 const DefaultTFStateFile = "terraform/terraform.tfstate" 21 22 // A Manager is used to manipulate whether Consul ACLs are enabled or disabled. 23 // Only works with TF provisioned clusters. 24 type Manager interface { 25 // Enable Consul ACLs in the Consul cluster. The Consul ACL master token 26 // associated with the Consul cluster is returned. 27 // 28 // A complete bootstrap process will take place if necessary. 29 // 30 // Once enabled, Consul ACLs can be disabled with Disable. 31 Enable(t *testing.T) string 32 33 // Disable Consul ACLs in the Consul Cluster. 34 // 35 // Once disabled, Consul ACLs can be re-enabled with Enable. 36 Disable(t *testing.T) 37 } 38 39 type tfManager struct { 40 serial int 41 } 42 43 func New(tfStateFile string) (*tfManager, error) { 44 serial, err := extractSerial(tfStateFile) 45 if err != nil { 46 return nil, err 47 } 48 return &tfManager{ 49 serial: serial, 50 }, nil 51 } 52 53 func (m *tfManager) Enable(t *testing.T) string { 54 // Run the consul ACL bootstrap script, which will store the master token 55 // in the deterministic path based on the TF state serial number. If the 56 // bootstrap process had already taken place, ACLs will be activated but 57 // without going through the bootstrap process again, re-using the already 58 // existing Consul ACL master token. 59 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) 60 defer cancel() 61 62 response, err := exec.CommandContext(ctx, 63 "consulacls/consul-acls-manage.sh", "enable").CombinedOutput() 64 require.NoError(t, err, "consul-acls-manage.sh failed: %v", string(response)) 65 fmt.Println(string(response)) 66 67 // Read the Consul ACL master token that was generated (or if the token 68 // already existed because the bootstrap process had already taken place, 69 // that one). 70 token, err := m.readToken() 71 require.NoError(t, err) 72 return token 73 } 74 75 type tfState struct { 76 Serial int `json:"serial"` 77 } 78 79 // extractSerial will parse the TF state file looking for the serial number. 80 func extractSerial(filename string) (int, error) { 81 if filename == "" { 82 filename = DefaultTFStateFile 83 } 84 b, err := ioutil.ReadFile(filename) 85 if err != nil { 86 return 0, fmt.Errorf("failed to extract TF serial: %w", err) 87 } 88 var state tfState 89 if err := json.Unmarshal(b, &state); err != nil { 90 return 0, fmt.Errorf("failed to extract TF serial: %w", err) 91 } 92 return state.Serial, nil 93 } 94 95 // tokenPath returns the expected path for the Consul ACL master token generated 96 // by the consul-acls-manage.sh bootstrap script for the current TF serial version. 97 func (m *tfManager) tokenPath() string { 98 return fmt.Sprintf("/tmp/e2e-consul-bootstrap-%d.token", m.serial) 99 } 100 101 func (m *tfManager) readToken() (string, error) { 102 b, err := ioutil.ReadFile(m.tokenPath()) 103 if err != nil { 104 return "", err 105 } 106 return strings.TrimSpace(string(b)), nil 107 } 108 109 func (m *tfManager) Disable(t *testing.T) { 110 // Run the consul ACL bootstrap script, which will modify the Consul Server 111 // ACL policies to disable ACLs, and then restart those agents. 112 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) 113 defer cancel() 114 115 response, err := exec.CommandContext(ctx, 116 "consulacls/consul-acls-manage.sh", "disable").CombinedOutput() 117 require.NoError(t, err) 118 fmt.Println(string(response)) 119 }