Skip to content

Commit cf3b744

Browse files
Support Rio to Harness v1
1 parent 7104c3b commit cf3b744

File tree

12 files changed

+1268
-4
lines changed

12 files changed

+1268
-4
lines changed

command/rio.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2022 Harness, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package command
16+
17+
import (
18+
"context"
19+
"flag"
20+
"fmt"
21+
"io/ioutil"
22+
"log"
23+
"os"
24+
25+
"github.com/drone/go-convert/convert/rio"
26+
27+
"github.com/google/subcommands"
28+
)
29+
30+
type Rio struct {
31+
name string
32+
proj string
33+
org string
34+
repoName string
35+
repoConn string
36+
kubeName string
37+
kubeConn string
38+
dockerConn string
39+
40+
downgrade bool
41+
beforeAfter bool
42+
}
43+
44+
func (*Rio) Name() string { return "rio" }
45+
func (*Rio) Synopsis() string { return "converts a rio pipeline" }
46+
func (*Rio) Usage() string {
47+
return `rio [-downgrade] <path to rio.yml>
48+
`
49+
}
50+
51+
func (c *Rio) SetFlags(f *flag.FlagSet) {
52+
f.BoolVar(&c.downgrade, "downgrade", false, "downgrade to the legacy yaml format")
53+
f.BoolVar(&c.beforeAfter, "before-after", false, "print the befor and after")
54+
55+
f.StringVar(&c.org, "org", "default", "harness organization")
56+
f.StringVar(&c.proj, "project", "default", "harness project")
57+
f.StringVar(&c.name, "pipeline", "default", "harness pipeline name")
58+
f.StringVar(&c.repoConn, "repo-connector", "", "repository connector")
59+
f.StringVar(&c.repoName, "repo-name", "", "repository name")
60+
f.StringVar(&c.kubeConn, "kube-connector", "", "kubernetes connector")
61+
f.StringVar(&c.kubeName, "kube-namespace", "", "kubernets namespace")
62+
f.StringVar(&c.dockerConn, "docker-connector", "", "dockerhub connector")
63+
}
64+
65+
func (c *Rio) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
66+
path := f.Arg(0)
67+
68+
fmt.Println("# v1 harness yaml is not supported for Rio pipelines. Converting to v0...")
69+
// if the user does not specify the path as
70+
// a command line arg, assume the default path.
71+
if path == "" {
72+
path = ".rio.yml"
73+
}
74+
75+
// open the rio yaml
76+
before, err := ioutil.ReadFile(path)
77+
if err != nil {
78+
log.Println(err)
79+
return subcommands.ExitFailure
80+
}
81+
82+
// convert the pipeline yaml from the rio
83+
// format to the harness yaml format.
84+
converter := rio.New(
85+
rio.WithDockerhub(c.dockerConn),
86+
rio.WithKubernetes(c.kubeName, c.kubeConn),
87+
rio.WithName(c.name),
88+
rio.WithIdentifier(c.name),
89+
rio.WithOrganization(c.org),
90+
rio.WithProject(c.proj),
91+
)
92+
after, err := converter.ConvertBytes(before)
93+
if err != nil {
94+
log.Println(err)
95+
return subcommands.ExitFailure
96+
}
97+
98+
// downgrade from the v1 harness yaml format
99+
// to the v0 harness yaml format.
100+
if c.downgrade {
101+
fmt.Println("# downgrade for Rio pipeline is not supported")
102+
//// downgrade to the v0 yaml
103+
//d := downgrader.New(
104+
// downgrader.WithCodebase(c.repoName, c.repoConn),
105+
// downgrader.WithDockerhub(c.dockerConn),
106+
// downgrader.WithKubernetes(c.kubeName, c.kubeName),
107+
// downgrader.WithName(c.name),
108+
// downgrader.WithOrganization(c.org),
109+
// downgrader.WithProject(c.proj),
110+
//)
111+
//after, err = d.Downgrade(after)
112+
//if err != nil {
113+
// log.Println(err)
114+
// return subcommands.ExitFailure
115+
//}
116+
}
117+
118+
if c.beforeAfter {
119+
os.Stdout.WriteString("---\n")
120+
os.Stdout.Write(before)
121+
os.Stdout.WriteString("\n---\n")
122+
}
123+
124+
os.Stdout.Write(after)
125+
126+
return subcommands.ExitSuccess
127+
}

convert/harness/yaml/pipeline.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ type (
6969
Spec *InfraSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
7070
}
7171

72-
// InfraSpec describes pipeline infastructure.
72+
// InfraSpec describes pipeline infrastructure.
7373
InfraSpec struct {
74-
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
75-
Conn string `json:"connectorRef,omitempty" yaml:"connectorRef,omitempty"`
74+
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
75+
Conn string `json:"connectorRef,omitempty" yaml:"connectorRef,omitempty"`
76+
AutomountServiceToken bool `json:"automountServiceAccountToken,omitempty" yaml:"automountServiceAccountToken,omitempty"`
77+
Os string `json:"os,omitempty" yaml:"os,omitempty"`
7678
}
7779

7880
Platform struct {

convert/harness/yaml/step.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ type (
101101
Reports []*Report `json:"reports,omitempty" yaml:"reports,omitempty"`
102102
Resources *Resources `json:"resources,omitempty" yaml:"resources,omitempty"`
103103
RunAsUser string `json:"runAsUser,omitempty" yaml:"runAsUser,omitempty"`
104-
Tags map[string]string `json:"tags,omitempty" yaml:"tags,omitempty"`
104+
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
105105
Target string `json:"target,omitempty" yaml:"target,omitempty"`
106106
}
107107

convert/rio/convert.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
package rio
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"os"
8+
"strings"
9+
10+
harness "github.com/drone/go-convert/convert/harness/yaml"
11+
rio "github.com/drone/go-convert/convert/rio/yaml"
12+
"github.com/drone/go-convert/internal/store"
13+
"gopkg.in/yaml.v3"
14+
)
15+
16+
// as we walk the yaml, we store a
17+
// snapshot of the current node and
18+
// its parents.
19+
type context struct {
20+
config *rio.Config
21+
}
22+
23+
// Converter converts a Rio pipeline to a Harness
24+
// v0 pipeline.
25+
type Converter struct {
26+
kubeEnabled bool
27+
kubeNamespace string
28+
kubeConnector string
29+
kubeOs string
30+
dockerhubConn string
31+
identifiers *store.Identifiers
32+
33+
pipelineId string
34+
pipelineName string
35+
pipelineOrg string
36+
pipelineProj string
37+
}
38+
39+
// New creates a new Converter that converts a Travis
40+
// pipeline to a Harness v1 pipeline.
41+
func New(options ...Option) *Converter {
42+
d := new(Converter)
43+
44+
// create the unique identifier store. this store
45+
// is used for registering unique identifiers to
46+
// prevent duplicate names, unique index violations.
47+
d.identifiers = store.New()
48+
49+
// loop through and apply the options.
50+
for _, option := range options {
51+
option(d)
52+
}
53+
54+
// set the default kubernetes namespace.
55+
if d.kubeNamespace == "" {
56+
d.kubeNamespace = "default"
57+
}
58+
59+
// set default kubernetes OS
60+
if d.kubeOs == "" {
61+
d.kubeOs = "Linux"
62+
}
63+
64+
// set the runtime to kubernetes if the kubernetes
65+
// connector is configured.
66+
if d.kubeConnector != "" {
67+
d.kubeEnabled = true
68+
}
69+
70+
// set default docker connector
71+
if d.dockerhubConn == "" {
72+
d.dockerhubConn = "account.harnessImage"
73+
}
74+
75+
return d
76+
}
77+
78+
// Convert converts a rio pipeline to v0.
79+
func (d *Converter) Convert(r io.Reader) ([]byte, error) {
80+
config, err := rio.Parse(r)
81+
if err != nil {
82+
return nil, err
83+
}
84+
return d.convert(&context{
85+
config: config,
86+
})
87+
}
88+
89+
// ConvertBytes converts a rio pipeline to v0.
90+
func (d *Converter) ConvertBytes(b []byte) ([]byte, error) {
91+
return d.Convert(
92+
bytes.NewBuffer(b),
93+
)
94+
}
95+
96+
// ConvertString converts a rio pipeline to v0.
97+
func (d *Converter) ConvertString(s string) ([]byte, error) {
98+
return d.Convert(
99+
bytes.NewBufferString(s),
100+
)
101+
}
102+
103+
// ConvertFile converts a rio pipeline to v0.
104+
func (d *Converter) ConvertFile(p string) ([]byte, error) {
105+
f, err := os.Open(p)
106+
if err != nil {
107+
return nil, err
108+
}
109+
defer f.Close()
110+
return d.Convert(f)
111+
}
112+
113+
func (d *Converter) convertRunStep(p rio.Pipeline) harness.StepRun {
114+
step := new(harness.StepRun)
115+
step.Env = p.Machine.Env
116+
command := ""
117+
for _, s := range p.Build.Steps {
118+
command += fmt.Sprintf("%s\n", s)
119+
}
120+
step.Command = command
121+
step.Shell = "Sh"
122+
step.ConnRef = d.dockerhubConn
123+
step.Image = p.Machine.BaseImage
124+
return *step
125+
}
126+
127+
func (d *Converter) convertDockerSteps(dockerfile rio.Dockerfile) []harness.StepDocker {
128+
steps := make([]harness.StepDocker, 0)
129+
for _, r := range dockerfile.Publish {
130+
step := new(harness.StepDocker)
131+
step.Context = dockerfile.Context
132+
step.Dockerfile = dockerfile.DockerfilePath
133+
step.Repo = r.Repo
134+
step.Tags = []string{dockerfile.Version}
135+
step.BuildsArgs = dockerfile.Env
136+
step.ConnectorRef = d.dockerhubConn
137+
steps = append(steps, *step)
138+
}
139+
return steps
140+
}
141+
142+
func (d *Converter) convertExecution(p rio.Pipeline) harness.Execution {
143+
execution := harness.Execution{}
144+
145+
executionSteps := make([]*harness.Steps, 0)
146+
// Append all commands in build attribute to one run step
147+
if len(p.Build.Steps) != 0 {
148+
steps := harness.Steps{
149+
Step: &harness.Step{
150+
Name: "run",
151+
ID: "run",
152+
Spec: d.convertRunStep(p),
153+
Type: "Run",
154+
},
155+
}
156+
executionSteps = append(executionSteps, &steps)
157+
}
158+
159+
dockerStepCounter := 1
160+
if len(p.Pkg.Dockerfile) != 0 {
161+
if !p.Pkg.Release {
162+
fmt.Println("# [WARN]: release=false is not supported")
163+
}
164+
165+
// Each entry in package.Dockerfile attribute is one build and push step
166+
for _, dockerfile := range p.Pkg.Dockerfile {
167+
// each dockerfile entry can have multiple repos
168+
dockerSteps := d.convertDockerSteps(dockerfile)
169+
170+
// Append all docker steps
171+
for _, dStep := range dockerSteps {
172+
steps := harness.Steps{
173+
Step: &harness.Step{
174+
Name: fmt.Sprintf("docker_build_and_push_%d", dockerStepCounter),
175+
ID: fmt.Sprintf("docker_build_and_push_%d", dockerStepCounter),
176+
Spec: &dStep,
177+
Type: "BuildAndPushDockerRegistry",
178+
},
179+
}
180+
executionSteps = append(executionSteps, &steps)
181+
dockerStepCounter++
182+
}
183+
}
184+
}
185+
execution.Steps = executionSteps
186+
return execution
187+
}
188+
189+
func (d *Converter) convertCIStage(p rio.Pipeline) harness.StageCI {
190+
stage := harness.StageCI{
191+
Execution: d.convertExecution(p),
192+
}
193+
if d.kubeEnabled {
194+
infra := harness.Infrastructure{
195+
Type: "KubernetesDirect",
196+
Spec: &harness.InfraSpec{
197+
Namespace: d.kubeNamespace,
198+
Conn: d.kubeConnector,
199+
AutomountServiceToken: true,
200+
Os: d.kubeOs,
201+
},
202+
}
203+
stage.Infrastructure = &infra
204+
}
205+
return stage
206+
}
207+
208+
func convertNameToID(name string) string {
209+
ID := strings.ReplaceAll(name, " ", "_")
210+
ID = strings.ReplaceAll(ID, "-", "_")
211+
return ID
212+
}
213+
214+
// converts converts a Travis pipeline to a Harness pipeline.
215+
func (d *Converter) convert(ctx *context) ([]byte, error) {
216+
// create the harness pipeline spec
217+
pipeline := &harness.Pipeline{}
218+
for _, p := range ctx.config.Pipelines {
219+
stage := harness.Stages{
220+
Stage: &harness.Stage{
221+
Name: p.Name,
222+
ID: convertNameToID(p.Name),
223+
Spec: d.convertCIStage(p),
224+
Type: "CI",
225+
},
226+
}
227+
pipeline.Stages = append(pipeline.Stages, &stage)
228+
}
229+
pipeline.Name = d.pipelineName
230+
pipeline.ID = d.pipelineId
231+
pipeline.Org = d.pipelineOrg
232+
pipeline.Project = d.pipelineProj
233+
234+
// create the harness pipeline resource
235+
config := &harness.Config{Pipeline: *pipeline}
236+
237+
// marshal the harness yaml
238+
out, err := yaml.Marshal(config)
239+
if err != nil {
240+
return nil, err
241+
}
242+
243+
return out, nil
244+
}

0 commit comments

Comments
 (0)