Skip to content

Commit 1fecebe

Browse files
Merge pull request #687 from vaidehi411/OSD-28274-dt-terraform-promotion
OSD-28274 Dynatrace Config terraform promotion
2 parents c223627 + e18f4da commit 1fecebe

9 files changed

+615
-27
lines changed

cmd/promote/dynatrace/dt_utils.go

+183
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,25 @@ package dynatrace
33
import (
44
"fmt"
55
"os"
6+
"os/exec"
7+
"path"
68
"path/filepath"
79
"sort"
810
"strings"
911
)
1012

1113
const (
1214
saasDynatraceDir = "data/services/osd-operators/cicd/saas/saas-dynatrace"
15+
moduleDir = "terraform/modules"
16+
ProductionDir = "terraform/redhat-aws/sd-sre/production"
17+
pattern = "git::https://gitlab.cee.redhat.com/service/dynatrace-config.git//terraform/modules/"
1318
)
1419

1520
var (
1621
ServicesSlice []string
1722
ServicesFilesMap = map[string]string{}
23+
ModulesSlice []string
24+
ModulesFilesMap = map[string]string{}
1825
)
1926

2027
func listServiceNames(appInterface AppInterface) error {
@@ -139,3 +146,179 @@ func GetSaasDir(component string) (string, error) {
139146
}
140147
return "", fmt.Errorf("saas directory for service %s not found", component)
141148
}
149+
150+
func listDynatraceModuleNames(dynatraceConfig DynatraceConfig) error {
151+
152+
baseDir := dynatraceConfig.GitDirectory
153+
_, err := GeModulesNames(baseDir, moduleDir)
154+
if err != nil {
155+
return err
156+
}
157+
158+
sort.Strings(ModulesSlice)
159+
fmt.Println("### Available terraform modules in dynatrace-config ###")
160+
for _, module := range ModulesSlice {
161+
fmt.Println(module)
162+
}
163+
164+
return nil
165+
}
166+
167+
func GeModulesNames(baseDir, dir string) ([]string, error) {
168+
169+
dirGlob := filepath.Join(baseDir, dir)
170+
filepaths, err := os.ReadDir(dirGlob)
171+
172+
if err != nil {
173+
return nil, err
174+
}
175+
176+
for _, filepath := range filepaths {
177+
if filepath.IsDir() {
178+
filename := filepath.Name()
179+
ModulesSlice = append(ModulesSlice, filename)
180+
ModulesFilesMap[filename] = filepath.Name()
181+
}
182+
}
183+
184+
return ModulesSlice, nil
185+
}
186+
187+
func ValidateModuleName(moduleName string) (string, error) {
188+
fmt.Printf("### Checking if service %s exists ###\n", moduleName)
189+
for _, service := range ModulesSlice {
190+
if service == moduleName {
191+
fmt.Printf("Module %s found in dynatrace-config\n", moduleName)
192+
return moduleName, nil
193+
}
194+
}
195+
196+
return moduleName, fmt.Errorf("service %s not found", moduleName)
197+
}
198+
199+
func updatePromotionGitHash(module string, dir string, promotionGitHash string) (string, error) {
200+
201+
fmt.Printf("Iterating over directory : %s", dir)
202+
items, _ := os.ReadDir(dir)
203+
for _, item := range items {
204+
fmt.Println("Production tenant: ", item.Name())
205+
if item.IsDir() {
206+
subDir := filepath.Join(dir, item.Name())
207+
subitems, _ := os.ReadDir(subDir)
208+
for _, subitem := range subitems {
209+
if subitem.IsDir() {
210+
fmt.Println("Folder : ", subitem.Name())
211+
subDir2 := filepath.Join(subDir, subitem.Name())
212+
subitems2, _ := os.ReadDir(subDir2)
213+
for _, subitem2 := range subitems2 {
214+
if !subitem2.IsDir() {
215+
filePath := filepath.Join(subDir2, subitem2.Name())
216+
extension := path.Ext(filePath)
217+
if extension == ".tf" {
218+
err := updateFileContent(filePath, module, promotionGitHash)
219+
if err != nil {
220+
return "", fmt.Errorf("error while writing files %s", err)
221+
}
222+
}
223+
}
224+
}
225+
}
226+
}
227+
}
228+
}
229+
230+
return "", nil
231+
}
232+
233+
func updateFileContent(filePath string, module, promotionGitHash string) error {
234+
var filename = filePath
235+
file, err := Open(filename)
236+
if err != nil {
237+
fmt.Println(err)
238+
}
239+
240+
ok := UpdateDefaultValue(file, module, promotionGitHash)
241+
if ok {
242+
err := Save(filename, file)
243+
if err != nil {
244+
return fmt.Errorf("Error while updating file %s: %s\n", filename, err)
245+
}
246+
fmt.Printf("File Updated :%s \n ", filePath)
247+
return nil
248+
}
249+
return nil
250+
}
251+
252+
func GetProductionDir(baseDir string) string {
253+
254+
dirGlob := filepath.Join(baseDir, ProductionDir)
255+
return dirGlob
256+
}
257+
258+
func getLatestGitHash(basedir, module string) (string, error) {
259+
260+
moduleFilePath := filepath.Join(basedir, moduleDir, module)
261+
cmd := exec.Command("git", "rev-list", "-n", "1", "HEAD", "--", moduleFilePath)
262+
output, err := cmd.Output()
263+
if err != nil {
264+
return "", fmt.Errorf("failed to get git hash: %v", err)
265+
}
266+
gitHash := strings.TrimSpace(string(output))
267+
fmt.Printf("The head githash for module %s is %s\n", module, gitHash)
268+
269+
return gitHash, nil
270+
}
271+
272+
func modulePromotion(dynatraceConfig DynatraceConfig, module string) error {
273+
274+
baseDir := dynatraceConfig.GitDirectory
275+
276+
_, err := GeModulesNames(baseDir, moduleDir)
277+
if err != nil {
278+
return err
279+
}
280+
281+
module, err = ValidateModuleName(module)
282+
if err != nil {
283+
return fmt.Errorf("Module Name : %s is not valid", module)
284+
}
285+
fmt.Printf("Module Name : %s is valid", module)
286+
287+
prodtenantDir := GetProductionDir(baseDir)
288+
289+
promotionGitHash, err := getLatestGitHash(baseDir, module)
290+
291+
if err != nil {
292+
return fmt.Errorf("failed to checkout and compare git hash: %v", err)
293+
}
294+
295+
fmt.Printf("Module: %s will be promoted to %s\n", module, promotionGitHash)
296+
297+
branchName := fmt.Sprintf("promote-%s-%s", module, promotionGitHash)
298+
299+
err = dynatraceConfig.UpdateDynatraceConfig(module, promotionGitHash, branchName)
300+
if err != nil {
301+
return fmt.Errorf("FAILURE: %v\n", err)
302+
}
303+
304+
promotePattern := pattern + module + "?ref=" + promotionGitHash
305+
306+
_, err = updatePromotionGitHash(module, prodtenantDir, promotePattern)
307+
308+
if err != nil {
309+
return err
310+
}
311+
commitLog := "Promote Module " + module + " to GitHash %s" + promotionGitHash
312+
fmt.Printf("commitLog: %v\n", commitLog)
313+
314+
err = dynatraceConfig.commitFiles(commitLog)
315+
if err != nil {
316+
return fmt.Errorf("failed to commit changes to app-interface: %w", err)
317+
}
318+
319+
fmt.Printf("The branch %s is ready to be pushed\n", branchName)
320+
fmt.Println("DT service:", module)
321+
fmt.Println("to:", promotionGitHash)
322+
fmt.Println("READY TO PUSH,", module, "promotion commit is ready locally")
323+
return nil
324+
}

cmd/promote/dynatrace/dynatrace.go

+72-27
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ import (
44
"fmt"
55
"os"
66

7-
git "github.com/openshift/osdctl/cmd/promote/git"
87
"github.com/spf13/cobra"
98
)
109

1110
type promoteDynatraceOptions struct {
1211
list bool
1312

14-
appInterfaceCheckoutDir string
15-
gitHash string
16-
component string
13+
appInterfaceCheckoutDir string
14+
gitHash string
15+
component string
16+
terraform bool
17+
module string
18+
dynatraceConfigCheckoutDir string
1719
}
1820

1921
// NewCmdPromote implements the promote command to promote services/operators
@@ -29,48 +31,91 @@ func NewCmdDynatrace() *cobra.Command {
2931
osdctl promote dynatrace --list
3032
3133
# Promote a dynatrace component
32-
osdctl promote dynatrace --component <component> --gitHash <git-hash>`,
34+
osdctl promote dynatrace --component <component> --gitHash <git-hash>
35+
36+
# List all dynatrace-config modules available for promotion
37+
osdctl promote dynatrace --terraform --list
38+
39+
# Promote a dynatrace module
40+
osdctl promote dynatrace --terraform --module=<module-name>`,
3341

3442
Run: func(cmd *cobra.Command, args []string) {
35-
ops.validateSaasFlow()
36-
appInterface := BootstrapOsdCtlForAppInterfaceAndServicePromotions(ops.appInterfaceCheckoutDir)
3743

38-
if ops.list {
39-
if ops.component != "" || ops.gitHash != "" {
40-
fmt.Printf("Error: --list cannot be used with any other flags\n\n")
44+
if ops.terraform {
45+
dynatraceConfig := DynatraceConfigPromotion(ops.dynatraceConfigCheckoutDir)
46+
if ops.list {
47+
ops.validateSaasFlow()
48+
if ops.component != "" || ops.gitHash != "" || ops.module != "" {
49+
fmt.Printf("Error: Please provide correct parameters \n\n")
50+
fmt.Printf("Please run 'osdctl promote dynatrace --terraform --list' to check available dynatrace-config modules for promotion.\n\n")
51+
cmd.Help()
52+
os.Exit(1)
53+
}
54+
listDynatraceModuleNames(dynatraceConfig)
55+
os.Exit(0)
56+
} else {
57+
if ops.module == "" {
58+
fmt.Printf("Error: Please provide correct parameters \n\n")
59+
fmt.Printf("Please run 'osdctl promote dynatrace --terraform --module=<module-name>' to check promote dynatrace-config module to latest ref.\n\n")
60+
cmd.Help()
61+
os.Exit(1)
62+
} else if ops.component != "" || ops.gitHash != "" {
63+
fmt.Printf("Error: Please provide correct parameters \n\n")
64+
fmt.Printf("Please run 'osdctl promote dynatrace --terraform --module=<module-name>' to check promote dynatrace-config module to latest ref.\n\n")
65+
cmd.Help()
66+
os.Exit(1)
67+
} else {
68+
err := modulePromotion(dynatraceConfig, ops.module)
69+
if err != nil {
70+
fmt.Printf("Error while promoting module: %v\n", err)
71+
os.Exit(1)
72+
}
73+
}
74+
}
75+
} else {
76+
77+
ops.validateSaasFlow()
78+
appInterface := BootstrapOsdCtlForAppInterfaceAndServicePromotions(ops.appInterfaceCheckoutDir)
79+
80+
if ops.list {
81+
if ops.component != "" || ops.gitHash != "" {
82+
fmt.Printf("Error: --list cannot be used with any other flags\n\n")
83+
cmd.Help()
84+
os.Exit(1)
85+
}
86+
listServiceNames(appInterface)
87+
os.Exit(0)
88+
}
89+
90+
if ops.component == "" {
91+
fmt.Printf("Error: Please provide dynatrace component to promote.\n\n")
92+
fmt.Printf("Please run 'osdctl promote dynatrace --list' to check available dynatrace components for promotion.\n\n")
4193
cmd.Help()
4294
os.Exit(1)
4395
}
44-
listServiceNames(appInterface)
45-
os.Exit(0)
46-
}
47-
48-
if ops.component == "" {
49-
fmt.Printf("Error: Please provide dynatrace component to promote.\n\n")
50-
fmt.Printf("Please run 'osdctl promote dynatrace --list' to check available dynatrace components for promotion.\n\n")
51-
cmd.Help()
52-
os.Exit(1)
53-
}
54-
err := servicePromotion(appInterface, ops.component, ops.gitHash)
55-
if err != nil {
56-
fmt.Printf("Error while promoting service: %v\n", err)
57-
os.Exit(1)
96+
err := servicePromotion(appInterface, ops.component, ops.gitHash)
97+
if err != nil {
98+
fmt.Printf("Error while promoting service: %v\n", err)
99+
os.Exit(1)
100+
}
58101
}
59-
60102
os.Exit(0)
61103
},
62104
}
63105

64106
promoteDynatraceCmd.Flags().BoolVarP(&ops.list, "list", "l", false, "List all SaaS services/operators")
65107
promoteDynatraceCmd.Flags().StringVarP(&ops.component, "component", "c", "", "Dynatrace component getting promoted")
66108
promoteDynatraceCmd.Flags().StringVarP(&ops.gitHash, "gitHash", "g", "", "Git hash of the SaaS service/operator commit getting promoted")
67-
promoteDynatraceCmd.Flags().StringVarP(&ops.appInterfaceCheckoutDir, "appInterfaceDir", "", "", "location of app-interfache checkout. Falls back to `pwd` and "+git.DefaultAppInterfaceDirectory())
109+
promoteDynatraceCmd.Flags().StringVarP(&ops.appInterfaceCheckoutDir, "appInterfaceDir", "", "", "location of app-interfache checkout. Falls back to `pwd` and "+DefaultAppInterfaceDirectory())
110+
promoteDynatraceCmd.Flags().BoolVarP(&ops.terraform, "terraform", "t", false, "deploy dynatrace-config terraform job")
111+
promoteDynatraceCmd.Flags().StringVarP(&ops.module, "module", "m", "", "module to promote")
112+
promoteDynatraceCmd.Flags().StringVarP(&ops.appInterfaceCheckoutDir, "dynatraceConfigDir", "", "", "location of dynatrace-config checkout. Falls back to `pwd` and "+DefaultDynatraceconfigDir())
68113

69114
return promoteDynatraceCmd
70115
}
71116

72117
func (o *promoteDynatraceOptions) validateSaasFlow() {
73-
if o.component == "" && o.gitHash == "" {
118+
if o.component == "" && o.gitHash == "" && !o.terraform {
74119
fmt.Printf("Usage: For dynatrace component, please provide --component and (optional) --gitHash\n")
75120
fmt.Printf("--component is the name of the component, i.e. dynatrace-dynakube\n")
76121
fmt.Printf("--gitHash is the target git commit for the dt component, if not specified defaults to HEAD of master\n\n")

0 commit comments

Comments
 (0)