Skip to content

Commit 108630d

Browse files
committed
refactor: argocdapp support push helm config files
Signed-off-by: Meng JiaFeng <[email protected]>
1 parent d28d88d commit 108630d

File tree

15 files changed

+294
-41
lines changed

15 files changed

+294
-41
lines changed

internal/pkg/configmanager/configmanager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ var _ = Describe("LoadConfig", func() {
144144
},
145145
},
146146
"branch": "main",
147-
"configLocation": "git@github.com:devstream-io/ci-template.git//github-actions",
147+
"configLocation": "https://raw.githubusercontent.com/devstream-io/ci-template/main/github-actions/workflows/main.yml",
148148
},
149149
"scm": RawOptions{
150150
"apiURL": "gitlab.com/some/path/to/your/api",

internal/pkg/configmanager/pipelinetemplate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ var _ = Describe("PipelineTemplate struct", func() {
231231
Options: RawOptions{
232232
"pipeline": RawOptions{
233233
"test": "testV",
234-
"configLocation": "git@github.com:devstream-io/ci-template.git//github-actions",
234+
"configLocation": "https://raw.githubusercontent.com/devstream-io/ci-template/main/github-actions/workflows/main.yml",
235235
},
236236
"scm": RawOptions{
237237
"url": cloneURL,

internal/pkg/plugin/argocdapp/argocdapp.go

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,53 @@ package argocdapp
22

33
import (
44
_ "embed"
5+
"fmt"
6+
7+
"github.com/devstream-io/devstream/internal/pkg/configmanager"
8+
"github.com/devstream-io/devstream/pkg/util/downloader"
9+
"github.com/devstream-io/devstream/pkg/util/scm"
10+
"github.com/devstream-io/devstream/pkg/util/scm/git"
511
)
612

7-
//go:embed tpl/argocd.tpl.yaml
8-
var templateFileLoc string
13+
//go:embed tpl/helm.tpl.yaml
14+
var helmApplicationConfig string
15+
16+
func pushArgocdConfigFiles(rawOptions configmanager.RawOptions) error {
17+
const createCommitMsg = "create argocdapp config files"
18+
const commitBranch = "feat-argocdapp-configs"
19+
opts, err := newOptions(rawOptions)
20+
if err != nil {
21+
return err
22+
}
23+
24+
// 1. init scm client for check argocdapp config exists and push argocdapp files
25+
scmInfo := scm.SCMInfo{CloneURL: opts.Source.RepoURL}
26+
scmClient, err := scmInfo.NewClientWithAuthFromScm()
27+
if err != nil {
28+
return err
29+
}
30+
31+
// 2. check argocdapp config existed in project repo
32+
configFileExist, err := opts.Source.checkPathExist(scmClient)
33+
if err != nil {
34+
return fmt.Errorf("argocdapp scm client get config path info failed: %w", err)
35+
}
36+
if configFileExist {
37+
return nil
38+
}
39+
40+
// 3. get argocd configFiles from remote
41+
const configLocation = downloader.ResourceLocation("https://github.com/devstream-io/ci-template.git//argocdapp/helm")
42+
gitFiles, err := opts.getArgocdDefaultConfigFiles(configLocation)
43+
if err != nil {
44+
return err
45+
}
46+
47+
// 4. push git files to ProjectRepo
48+
_, err = scmClient.PushFiles(&git.CommitInfo{
49+
CommitMsg: createCommitMsg,
50+
GitFileMap: gitFiles,
51+
CommitBranch: commitBranch,
52+
}, true)
53+
return err
54+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package argocdapp_test
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestCommon(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Plugin Argocdapp Suite")
13+
}

internal/pkg/plugin/argocdapp/create.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ func Create(options configmanager.RawOptions) (statemanager.ResourceStatus, erro
1717
validate,
1818
},
1919
ExecuteOperations: installer.ExecuteOperations{
20-
kubectl.ProcessByContent(kubectlUtil.Create, templateFileLoc),
20+
pushArgocdConfigFiles,
21+
kubectl.ProcessByContent(kubectlUtil.Create, helmApplicationConfig),
2122
},
2223
GetStatusOperation: getStaticStatus,
2324
}

internal/pkg/plugin/argocdapp/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func Delete(options configmanager.RawOptions) (bool, error) {
1414
validate,
1515
},
1616
ExecuteOperations: installer.ExecuteOperations{
17-
kubectl.ProcessByContent(kubectlUtil.Delete, templateFileLoc),
17+
kubectl.ProcessByContent(kubectlUtil.Delete, helmApplicationConfig),
1818
},
1919
}
2020

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,90 @@
11
package argocdapp
22

33
import (
4+
"path/filepath"
5+
46
"github.com/mitchellh/mapstructure"
57

68
"github.com/devstream-io/devstream/internal/pkg/configmanager"
9+
"github.com/devstream-io/devstream/pkg/util/downloader"
10+
"github.com/devstream-io/devstream/pkg/util/file"
11+
"github.com/devstream-io/devstream/pkg/util/log"
12+
"github.com/devstream-io/devstream/pkg/util/scm"
13+
"github.com/devstream-io/devstream/pkg/util/scm/git"
14+
"github.com/devstream-io/devstream/pkg/util/template"
715
)
816

9-
// Param is the struct for parameters used by the argocdapp package.
10-
type Options struct {
11-
App App
12-
Destination Destination
13-
Source Source
17+
// options is the struct for parameters used by the argocdapp package.
18+
type options struct {
19+
App *app `mapstructure:"app" validate:"required"`
20+
Destination *destination `mapstructure:"destination"`
21+
Source *source `mapstructure:"source" validate:"required"`
22+
ImageRepo imageRepo `mapstructure:"imageRepo"`
23+
}
24+
25+
type imageRepo struct {
26+
Address string `mapstructure:"address"`
27+
Tag string `mapstructure:"tag"`
1428
}
1529

16-
// App is the struct for an ArgoCD app.
17-
type App struct {
18-
Name string `validate:"required,dns1123subdomain"`
19-
Namespace string
30+
// app is the struct for an ArgoCD app.
31+
type app struct {
32+
Name string `mapstructure:"name" validate:"dns1123subdomain"`
33+
Namespace string `mapstructure:"namespace"`
2034
}
2135

22-
// Destination is the struct for the destination of an ArgoCD app.
23-
type Destination struct {
24-
Server string
25-
Namespace string
36+
// destination is the struct for the destination of an ArgoCD app.
37+
type destination struct {
38+
Server string `mapstructure:"server"`
39+
Namespace string `mapstructure:"namespace"`
2640
}
2741

28-
// Source is the struct for the source of an ArgoCD app.
29-
type Source struct {
30-
Valuefile string
31-
Path string `validate:"required"`
32-
RepoURL string `validate:"required"`
42+
// source is the struct for the source of an ArgoCD app.
43+
type source struct {
44+
Valuefile string `mapstructure:"valuefile"`
45+
Path string `mapstructure:"path" validate:"required"`
46+
RepoURL string `mapstructure:"repoURL" validate:"required"`
3347
}
3448

35-
// / NewOptions create options by raw options
36-
func NewOptions(options configmanager.RawOptions) (Options, error) {
37-
var opts Options
38-
if err := mapstructure.Decode(options, &opts); err != nil {
49+
// / newOptions create options by raw options
50+
func newOptions(rawOptions configmanager.RawOptions) (options, error) {
51+
var opts options
52+
if err := mapstructure.Decode(rawOptions, &opts); err != nil {
3953
return opts, err
4054
}
4155
return opts, nil
4256
}
57+
58+
// checkPathExist will check argocdapp config already exist
59+
func (s *source) checkPathExist(scmClient scm.ClientOperation) (bool, error) {
60+
existFiles, err := scmClient.GetPathInfo(s.Path)
61+
if err != nil {
62+
return false, err
63+
}
64+
if len(existFiles) > 0 {
65+
log.Debugf("argocdapp check config path %s already exist, pass...", s.Path)
66+
return true, nil
67+
}
68+
return false, nil
69+
}
70+
71+
func (o *options) getArgocdDefaultConfigFiles(configLocation downloader.ResourceLocation) (git.GitFileContentMap, error) {
72+
// 1. get configs from remote url
73+
configFiles, err := configLocation.Download()
74+
if err != nil {
75+
return nil, err
76+
}
77+
// 2. get file content
78+
fContentFunc := func(filePath string) ([]byte, error) {
79+
renderContent, err := template.New().FromLocalFile(filePath).SetDefaultRender("argocd config", o).Render()
80+
if err != nil {
81+
return nil, err
82+
}
83+
return []byte(renderContent), nil
84+
}
85+
fNameFunc := func(filePath, srcPath string) string {
86+
relativePath, _ := filepath.Rel(srcPath, filePath)
87+
return filepath.Join(o.Source.Path, relativePath)
88+
}
89+
return file.GetFileMap(configFiles, file.DirFileFilterDefaultFunc, fNameFunc, fContentFunc)
90+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package argocdapp
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
8+
. "github.com/onsi/ginkgo/v2"
9+
. "github.com/onsi/gomega"
10+
"github.com/onsi/gomega/ghttp"
11+
12+
"github.com/devstream-io/devstream/pkg/util/downloader"
13+
"github.com/devstream-io/devstream/pkg/util/scm"
14+
"github.com/devstream-io/devstream/pkg/util/scm/git"
15+
)
16+
17+
var _ = Describe("source struct", func() {
18+
var (
19+
scmClient scm.ClientOperation
20+
errMsg string
21+
s *source
22+
)
23+
BeforeEach(func() {
24+
s = &source{
25+
Path: "test_path",
26+
}
27+
})
28+
Context("checkPathExist method", func() {
29+
When("scm client error", func() {
30+
BeforeEach(func() {
31+
errMsg = "scm get path error"
32+
scmClient = &scm.MockScmClient{
33+
GetPathInfoError: errors.New(errMsg),
34+
}
35+
})
36+
It("should return error", func() {
37+
_, err := s.checkPathExist(scmClient)
38+
Expect(err).Should(HaveOccurred())
39+
Expect(err.Error()).Should(Equal(errMsg))
40+
})
41+
})
42+
When("path list is empty", func() {
43+
BeforeEach(func() {
44+
scmClient = &scm.MockScmClient{
45+
GetPathInfoReturnValue: []*git.RepoFileStatus{},
46+
}
47+
})
48+
It("should return false", func() {
49+
exist, err := s.checkPathExist(scmClient)
50+
Expect(err).ShouldNot(HaveOccurred())
51+
Expect(exist).Should(BeFalse())
52+
})
53+
})
54+
When("path list is not empty", func() {
55+
BeforeEach(func() {
56+
scmClient = &scm.MockScmClient{
57+
GetPathInfoReturnValue: []*git.RepoFileStatus{
58+
{Path: "gg"},
59+
},
60+
}
61+
})
62+
It("should return true", func() {
63+
exist, err := s.checkPathExist(scmClient)
64+
Expect(err).ShouldNot(HaveOccurred())
65+
Expect(exist).Should(BeTrue())
66+
})
67+
})
68+
})
69+
})
70+
71+
var _ = Describe("options struct", func() {
72+
var (
73+
configLocation downloader.ResourceLocation
74+
o *options
75+
s *ghttp.Server
76+
pathName, reqPath string
77+
)
78+
BeforeEach(func() {
79+
s = ghttp.NewServer()
80+
o = &options{
81+
App: &app{
82+
Name: "test",
83+
},
84+
Source: &source{
85+
Path: "test_path",
86+
RepoURL: "https://test.com/owner/repo",
87+
},
88+
}
89+
pathName = "/configLoc"
90+
reqPath = fmt.Sprintf("%s%s", s.URL(), pathName)
91+
s.RouteToHandler("HEAD", pathName, func(w http.ResponseWriter, r *http.Request) {
92+
fmt.Fprint(w, "ok")
93+
})
94+
})
95+
Context("getArgocdDefaultConfigFiles method", func() {
96+
When("can't get location", func() {
97+
BeforeEach(func() {
98+
s.RouteToHandler("GET", pathName, ghttp.CombineHandlers(
99+
ghttp.VerifyRequest("GET", pathName),
100+
ghttp.RespondWith(http.StatusNotFound, ""),
101+
))
102+
configLocation = downloader.ResourceLocation(reqPath)
103+
})
104+
It("should return error", func() {
105+
_, err := o.getArgocdDefaultConfigFiles(configLocation)
106+
Expect(err).Should(HaveOccurred())
107+
Expect(err.Error()).Should(ContainSubstring("bad response code: 404"))
108+
})
109+
})
110+
When("all valid", func() {
111+
BeforeEach(func() {
112+
configFileContent := `
113+
app:
114+
name: [[ .App.Name ]]`
115+
s.RouteToHandler("GET", pathName, ghttp.CombineHandlers(
116+
ghttp.VerifyRequest("GET", pathName),
117+
ghttp.RespondWith(http.StatusOK, configFileContent),
118+
))
119+
configLocation = downloader.ResourceLocation(reqPath)
120+
})
121+
It("should return valid data", func() {
122+
data, err := o.getArgocdDefaultConfigFiles(configLocation)
123+
Expect(err).ShouldNot(HaveOccurred())
124+
Expect(data).ShouldNot(BeEmpty())
125+
})
126+
})
127+
})
128+
AfterEach(func() {
129+
s.Close()
130+
})
131+
})

internal/pkg/plugin/argocdapp/state.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func getStaticStatus(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
14-
opts, err := NewOptions(options)
14+
opts, err := newOptions(options)
1515
if err != nil {
1616
return nil, err
1717
}
@@ -37,7 +37,7 @@ func getStaticStatus(options configmanager.RawOptions) (statemanager.ResourceSta
3737
}
3838

3939
func getDynamicStatus(options configmanager.RawOptions) (statemanager.ResourceStatus, error) {
40-
opts, err := NewOptions(options)
40+
opts, err := newOptions(options)
4141
if err != nil {
4242
return nil, err
4343
}
File renamed without changes.

0 commit comments

Comments
 (0)