|
| 1 | +package parameters |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "flag" |
| 6 | + "fmt" |
| 7 | + "log" |
| 8 | + "os" |
| 9 | + "os/signal" |
| 10 | + |
| 11 | + "github.com/aws/aws-sdk-go-v2/service/ssm" |
| 12 | + "github.com/aws/aws-sdk-go-v2/service/ssm/types" |
| 13 | +) |
| 14 | + |
| 15 | +// Fetch values for parameters |
| 16 | +// Inputs: |
| 17 | +// chunks: list of lists with parameter key values |
| 18 | +func FetchParameters(ctx context.Context, api SSMGetParametersAPI, chunks [][]string, flags Flags) []Parameter { |
| 19 | + var parameters []Parameter |
| 20 | + |
| 21 | + for _, chunk := range chunks { |
| 22 | + getInput := &ssm.GetParametersInput{ |
| 23 | + Names: chunk, |
| 24 | + } |
| 25 | + results, err := ExecGetParameters(ctx, api, getInput) |
| 26 | + if err != nil { |
| 27 | + log.Fatal(err) |
| 28 | + } |
| 29 | + |
| 30 | + for _, p := range results.Parameters { |
| 31 | + parameter := Parameter{ |
| 32 | + Name: *p.Name, Value: *p.Value, |
| 33 | + } |
| 34 | + if flags.Export { |
| 35 | + parameter.Export = "export " |
| 36 | + } |
| 37 | + parameters = append(parameters, parameter) |
| 38 | + } |
| 39 | + } |
| 40 | + log.Println("Retrieved values for parameters") |
| 41 | + |
| 42 | + return parameters |
| 43 | +} |
| 44 | + |
| 45 | +// Fetch list of parameter keys from AWS with defined filters |
| 46 | +func FetchKeysOfParameters( |
| 47 | + ctx context.Context, |
| 48 | + api SSMDescribeParametersAPI, |
| 49 | + flags Flags, |
| 50 | +) []string { |
| 51 | + var parameters []string |
| 52 | + |
| 53 | + // Set parameter filters |
| 54 | + filterKey := "tag:Product" |
| 55 | + parameterFilters := []types.ParameterStringFilter{ |
| 56 | + { |
| 57 | + Key: &filterKey, |
| 58 | + Values: []string{flags.ProductTag}, |
| 59 | + }, |
| 60 | + } |
| 61 | + describeInput := &ssm.DescribeParametersInput{ |
| 62 | + MaxResults: int32(flags.MaxResults), |
| 63 | + ParameterFilters: parameterFilters, |
| 64 | + } |
| 65 | + n := 0 |
| 66 | + for { |
| 67 | + // Fetch list of parameter keys |
| 68 | + results, err := ExecDescribeParameters(ctx, api, describeInput) |
| 69 | + if err != nil { |
| 70 | + log.Fatal(err) |
| 71 | + } |
| 72 | + for _, p := range results.Parameters { |
| 73 | + parameters = append(parameters, *p.Name) |
| 74 | + } |
| 75 | + |
| 76 | + // If there are no more parameters break |
| 77 | + if results.NextToken == nil { |
| 78 | + break |
| 79 | + } |
| 80 | + describeInput.NextToken = *&results.NextToken |
| 81 | + |
| 82 | + n++ |
| 83 | + if n >= 50 { |
| 84 | + log.Fatal("To many iterations over DescribeParameters loop") |
| 85 | + } |
| 86 | + } |
| 87 | + log.Printf("Retrieved %d parameters", len(parameters)) |
| 88 | + |
| 89 | + return parameters |
| 90 | +} |
| 91 | + |
| 92 | +// Split list of reports on nested lists |
| 93 | +func GenerateChunks(flatSlice []string, chunkSize int) [][]string { |
| 94 | + if len(flatSlice) == 0 { |
| 95 | + return [][]string{} |
| 96 | + } |
| 97 | + |
| 98 | + chunks := make([][]string, 0, len(flatSlice)/chunkSize+1) |
| 99 | + |
| 100 | + for i, v := range flatSlice { |
| 101 | + if i%chunkSize == 0 { |
| 102 | + chunks = append(chunks, make([]string, 0, chunkSize)) |
| 103 | + } |
| 104 | + chunks[len(chunks)-1] = append(chunks[len(chunks)-1], v) |
| 105 | + } |
| 106 | + |
| 107 | + return chunks |
| 108 | +} |
| 109 | + |
| 110 | +// WriteToFile generate or update existing file and |
| 111 | +// flash to it environment variables |
| 112 | +func WriteToFile(parameters []Parameter, outfile string, update bool, export bool) { |
| 113 | + flag := os.O_TRUNC | os.O_CREATE | os.O_WRONLY |
| 114 | + if update { |
| 115 | + flag = os.O_APPEND | os.O_CREATE | os.O_WRONLY |
| 116 | + } |
| 117 | + |
| 118 | + f, err := os.OpenFile( |
| 119 | + outfile, |
| 120 | + flag, |
| 121 | + 0644, |
| 122 | + ) |
| 123 | + if err != nil { |
| 124 | + log.Fatalf("Unable to open file %s, error: %s", outfile, err) |
| 125 | + } |
| 126 | + defer f.Close() |
| 127 | + |
| 128 | + parametersStr := "" |
| 129 | + for _, p := range parameters { |
| 130 | + parametersStr += fmt.Sprintf("%s%s=%s\n", p.Export, p.Name, p.Value) |
| 131 | + } |
| 132 | + if _, err := f.WriteString(parametersStr); err != nil { |
| 133 | + log.Fatalf("Unable to write to file %s, error: %s", outfile, err) |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +// HandleSignals process Ctrl+C and all script interruptions |
| 138 | +func HandleSignals(cancel context.CancelFunc) { |
| 139 | + sigCh := make(chan os.Signal) |
| 140 | + signal.Notify(sigCh, os.Interrupt) |
| 141 | + for { |
| 142 | + sig := <-sigCh |
| 143 | + switch sig { |
| 144 | + case os.Interrupt: |
| 145 | + cancel() |
| 146 | + return |
| 147 | + } |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +func Extract() { |
| 152 | + var flags Flags |
| 153 | + flag.BoolVar(&flags.Export, "export", false, "Add prefix 'export' to each parameter") |
| 154 | + flag.IntVar(&flags.MaxResults, "max", 3, "The maximum number of items to return for call to AWS") |
| 155 | + flag.StringVar(&flags.Outfile, "outfile", "", "Output file where parameters will be saved") |
| 156 | + flag.StringVar(&flags.ProductTag, "product", "", "Product tag") |
| 157 | + flag.BoolVar(&flags.Update, "update", false, "Update existing file if exists (by default the file will be overwritten)") |
| 158 | + flag.Parse() |
| 159 | + |
| 160 | + if flags.ProductTag == "" { |
| 161 | + log.Fatalln("Please specify the tag of product") |
| 162 | + } |
| 163 | + |
| 164 | + ctx, cancel := context.WithCancel(context.Background()) |
| 165 | + go HandleSignals(cancel) |
| 166 | + |
| 167 | + client := InitAWSClient(ctx) |
| 168 | + |
| 169 | + parameterKeys := FetchKeysOfParameters(ctx, client, flags) |
| 170 | + |
| 171 | + // Split slice of parameter keys to chunks by 10 (max len allowed by AWS) |
| 172 | + // and fetch values for required parameters |
| 173 | + parameterKeyChunks := GenerateChunks(parameterKeys, 10) |
| 174 | + if len(parameterKeyChunks) == 0 { |
| 175 | + log.Fatalln("Nothing to generate, empty slice provided") |
| 176 | + } |
| 177 | + parameters := FetchParameters(ctx, client, parameterKeyChunks, flags) |
| 178 | + |
| 179 | + if flags.Outfile != "" { |
| 180 | + WriteToFile(parameters, flags.Outfile, flags.Update, flags.Export) |
| 181 | + } else { |
| 182 | + for _, p := range parameters { |
| 183 | + fmt.Printf("%s%s=%s\n", p.Export, p.Name, p.Value) |
| 184 | + } |
| 185 | + } |
| 186 | +} |
0 commit comments