Skip to content

Commit 66436ca

Browse files
authored
Generate azure pipelines (#337)
1 parent 457eb25 commit 66436ca

File tree

6 files changed

+302
-4
lines changed

6 files changed

+302
-4
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package azurePipelines
2+
3+
import (
4+
"embed"
5+
"fmt"
6+
"io/fs"
7+
"path"
8+
9+
"github.com/Azure/draft/pkg/config"
10+
"github.com/Azure/draft/pkg/embedutils"
11+
"github.com/Azure/draft/pkg/osutil"
12+
"github.com/Azure/draft/pkg/templatewriter"
13+
log "github.com/sirupsen/logrus"
14+
"gopkg.in/yaml.v3"
15+
)
16+
17+
const (
18+
pipelineParentDirName = "azurePipelines"
19+
aksPipelineTemplateFileName = "azure-kubernetes-service.yaml"
20+
configFileName = "draft.yaml"
21+
pipelineNameVar = "PIPELINENAME"
22+
)
23+
24+
type AzurePipelines struct {
25+
pipelines map[string]fs.DirEntry
26+
configs map[string]*config.DraftConfig
27+
dest string
28+
pipelineTemplates embed.FS
29+
}
30+
31+
func CreatePipelinesFromEmbedFS(pipelineTemplates embed.FS, dest string) (*AzurePipelines, error) {
32+
pipelineMap, err := embedutils.EmbedFStoMap(pipelineTemplates, "azurePipelines")
33+
if err != nil {
34+
return nil, fmt.Errorf("error creating map from embedded FS: %w", err)
35+
}
36+
37+
p := &AzurePipelines{
38+
pipelines: pipelineMap,
39+
dest: dest,
40+
configs: make(map[string]*config.DraftConfig),
41+
pipelineTemplates: pipelineTemplates,
42+
}
43+
p.populateConfigs()
44+
45+
return p, nil
46+
47+
}
48+
49+
func (p *AzurePipelines) populateConfigs() {
50+
for _, val := range p.pipelines {
51+
draftConfig, err := p.loadConfig(val.Name())
52+
if err != nil {
53+
log.Debugf("error loading draftConfig for pipeline of deploy type %s: %v", val.Name(), err)
54+
draftConfig = &config.DraftConfig{}
55+
}
56+
p.configs[val.Name()] = draftConfig
57+
}
58+
}
59+
60+
func (p *AzurePipelines) GetConfig(deployType string) (*config.DraftConfig, error) {
61+
val, ok := p.configs[deployType]
62+
if !ok {
63+
return nil, fmt.Errorf("deploy type %s unsupported", deployType)
64+
}
65+
return val, nil
66+
}
67+
68+
func (p *AzurePipelines) loadConfig(deployType string) (*config.DraftConfig, error) {
69+
val, ok := p.pipelines[deployType]
70+
if !ok {
71+
return nil, fmt.Errorf("deploy type %s unsupported", deployType)
72+
}
73+
74+
configPath := path.Join(pipelineParentDirName, val.Name(), configFileName)
75+
configBytes, err := fs.ReadFile(p.pipelineTemplates, configPath)
76+
if err != nil {
77+
return nil, fmt.Errorf("error reading config file: %w", err)
78+
}
79+
80+
var draftConfig config.DraftConfig
81+
if err = yaml.Unmarshal(configBytes, &draftConfig); err != nil {
82+
return nil, fmt.Errorf("error unmarshalling config file: %w", err)
83+
}
84+
85+
return &draftConfig, nil
86+
}
87+
88+
func (p *AzurePipelines) overrideFilename(draftConfig *config.DraftConfig, srcDir string) error {
89+
if draftConfig.FileNameOverrideMap == nil {
90+
draftConfig.FileNameOverrideMap = make(map[string]string)
91+
}
92+
pipelineVar, err := draftConfig.GetVariable(pipelineNameVar)
93+
if err != nil {
94+
return fmt.Errorf("error getting pipeline name variable: %w", err)
95+
}
96+
97+
if err = fs.WalkDir(p.pipelineTemplates, srcDir, func(path string, d fs.DirEntry, err error) error {
98+
if err != nil {
99+
return err
100+
}
101+
if d.Name() == aksPipelineTemplateFileName {
102+
draftConfig.FileNameOverrideMap[d.Name()] = pipelineVar.Value + ".yaml"
103+
}
104+
return nil
105+
}); err != nil {
106+
return fmt.Errorf("error walking through source directory: %w", err)
107+
}
108+
109+
return nil
110+
}
111+
112+
func (p *AzurePipelines) CreatePipelineFiles(deployType string, draftConfig *config.DraftConfig, templateWriter templatewriter.TemplateWriter) error {
113+
val, ok := p.pipelines[deployType]
114+
if !ok {
115+
return fmt.Errorf("deploy type %s currently unsupported for azure pipeline", deployType)
116+
}
117+
srcDir := path.Join(pipelineParentDirName, val.Name())
118+
log.Debugf("source directory of pipeline template: %s", srcDir)
119+
120+
if err := p.overrideFilename(draftConfig, srcDir); err != nil {
121+
return fmt.Errorf("error overriding filename: %w", err)
122+
}
123+
124+
if err := draftConfig.ApplyDefaultVariables(); err != nil {
125+
return fmt.Errorf("error applying default variables: %w", err)
126+
}
127+
128+
if err := osutil.CopyDir(p.pipelineTemplates, srcDir, p.dest, draftConfig, templateWriter); err != nil {
129+
return fmt.Errorf("error copying pipeline files: %w", err)
130+
}
131+
132+
return nil
133+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package azurePipelines
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/Azure/draft/pkg/config"
9+
"github.com/Azure/draft/pkg/templatewriter/writers"
10+
"github.com/Azure/draft/template"
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func TestCreatePipelines(t *testing.T) {
15+
var pipelineFilePath string
16+
templateWriter := &writers.LocalFSWriter{}
17+
18+
tests := []struct {
19+
name string
20+
deployType string
21+
shouldError bool
22+
setConfig func(dc *config.DraftConfig)
23+
cleanUp func(tempDir string)
24+
}{
25+
{
26+
name: "kustomize_default_path",
27+
deployType: "kustomize",
28+
shouldError: false,
29+
},
30+
{
31+
name: "kustomize_given_path",
32+
deployType: "kustomize",
33+
shouldError: false,
34+
setConfig: func(dc *config.DraftConfig) {
35+
dc.SetVariable("KUSTOMIZEPATH", "test/kustomize/overlays/production")
36+
},
37+
},
38+
{
39+
name: "manifests_default_path",
40+
deployType: "manifests",
41+
shouldError: false,
42+
setConfig: func(dc *config.DraftConfig) {
43+
dc.SetVariable("PIPELINENAME", "some-other-name")
44+
},
45+
},
46+
{
47+
name: "manifests_custom_path",
48+
deployType: "manifests",
49+
shouldError: false,
50+
setConfig: func(dc *config.DraftConfig) {
51+
dc.SetVariable("MANIFESTPATH", "test/manifests")
52+
},
53+
},
54+
{
55+
name: "invalid",
56+
deployType: "invalid",
57+
shouldError: true,
58+
},
59+
{
60+
name: "missing_config",
61+
deployType: "kustomize",
62+
shouldError: true,
63+
setConfig: func(dc *config.DraftConfig) {
64+
// removing the last variable from draftConfig
65+
dc.Variables = dc.Variables[:len(dc.Variables)-1]
66+
},
67+
},
68+
}
69+
70+
for _, tt := range tests {
71+
draftConfig := newDraftConfig()
72+
73+
tempDir, err := os.MkdirTemp(".", "testTempDir")
74+
assert.Nil(t, err)
75+
76+
if tt.setConfig != nil {
77+
tt.setConfig(draftConfig)
78+
}
79+
80+
pipelines, err := CreatePipelinesFromEmbedFS(template.AzurePipelines, tempDir)
81+
assert.Nil(t, err)
82+
83+
err = pipelines.CreatePipelineFiles(tt.deployType, draftConfig, templateWriter)
84+
85+
pipelineFilePath = fmt.Sprintf("%s/.pipelines/%s", tempDir, aksPipelineTemplateFileName)
86+
if val, ok := draftConfig.FileNameOverrideMap[aksPipelineTemplateFileName]; ok {
87+
pipelineFilePath = fmt.Sprintf("%s/.pipelines/%s", tempDir, val)
88+
}
89+
90+
if tt.shouldError {
91+
assert.NotNil(t, err)
92+
_, err = os.Stat(pipelineFilePath)
93+
assert.Equal(t, os.IsNotExist(err), true)
94+
} else {
95+
assert.Nil(t, err)
96+
_, err = os.Stat(pipelineFilePath)
97+
assert.Nil(t, err)
98+
}
99+
100+
err = os.RemoveAll(tempDir)
101+
assert.Nil(t, err)
102+
}
103+
}
104+
105+
func newDraftConfig() *config.DraftConfig {
106+
return &config.DraftConfig{
107+
Variables: []*config.BuilderVar{
108+
{
109+
Name: "PIPELINENAME",
110+
Value: "testPipeline",
111+
},
112+
{
113+
Name: "BRANCHNAME",
114+
Default: config.BuilderVarDefault{
115+
Value: "main",
116+
},
117+
},
118+
{
119+
Name: "ARMSERVICECONNECTION",
120+
Value: "testServiceConnection",
121+
},
122+
{
123+
Name: "AZURECONTAINERREGISTRY",
124+
Value: "testACR",
125+
},
126+
{
127+
Name: "CONTAINERNAME",
128+
Value: "testContainer",
129+
},
130+
{
131+
Name: "CLUSTERRESOURCEGROUP",
132+
Value: "testRG",
133+
},
134+
{
135+
Name: "ACRRESOURCEGROUP",
136+
Value: "testACRRG",
137+
},
138+
{
139+
Name: "CLUSTERNAME",
140+
Value: "testCluster",
141+
},
142+
{
143+
Name: "KUSTOMIZEPATH",
144+
Default: config.BuilderVarDefault{
145+
Value: "kustomize/overlays/production",
146+
},
147+
},
148+
{
149+
Name: "MANIFESTPATH",
150+
Default: config.BuilderVarDefault{
151+
Value: "manifests",
152+
},
153+
},
154+
{
155+
Name: "NAMESPACE",
156+
Value: "testNamespace",
157+
},
158+
},
159+
}
160+
}

pkg/config/draftconfig.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import (
99

1010
// TODO: remove Name Overrides since we don't need them anymore
1111
type DraftConfig struct {
12-
DisplayName string `yaml:"displayName"`
13-
NameOverrides []FileNameOverride `yaml:"nameOverrides"`
14-
Variables []*BuilderVar `yaml:"variables"`
12+
DisplayName string `yaml:"displayName"`
13+
NameOverrides []FileNameOverride `yaml:"nameOverrides"`
14+
Variables []*BuilderVar `yaml:"variables"`
15+
FileNameOverrideMap map[string]string `yaml:"filenameOverrideMap"`
1516

1617
nameOverrideMap map[string]string
1718
}

pkg/osutil/osutil.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,12 @@ func CopyDir(
9494
continue
9595
}
9696

97+
fileName := f.Name()
98+
if overrideName, ok := draftConfig.FileNameOverrideMap[f.Name()]; ok {
99+
fileName = overrideName
100+
}
97101
srcPath := path.Join(src, f.Name())
98-
destPath := path.Join(dest, f.Name())
102+
destPath := path.Join(dest, fileName)
99103
log.Debugf("Source path: %s Dest path: %s", srcPath, destPath)
100104

101105
if f.IsDir() {

template/azurePipelines/kustomize/.pipelines/azure-kubernetes-service-kustomize.yaml renamed to template/azurePipelines/kustomize/.pipelines/azure-kubernetes-service.yaml

File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)