Skip to content

Commit

Permalink
Support Rio to Harness v1
Browse files Browse the repository at this point in the history
  • Loading branch information
rutvijmehta-harness committed Sep 25, 2023
1 parent 7104c3b commit cf3b744
Show file tree
Hide file tree
Showing 12 changed files with 1,268 additions and 4 deletions.
127 changes: 127 additions & 0 deletions command/rio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2022 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package command

import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"

"github.com/drone/go-convert/convert/rio"

"github.com/google/subcommands"
)

type Rio struct {
name string
proj string
org string
repoName string
repoConn string
kubeName string
kubeConn string
dockerConn string

downgrade bool
beforeAfter bool
}

func (*Rio) Name() string { return "rio" }
func (*Rio) Synopsis() string { return "converts a rio pipeline" }
func (*Rio) Usage() string {
return `rio [-downgrade] <path to rio.yml>
`
}

func (c *Rio) SetFlags(f *flag.FlagSet) {
f.BoolVar(&c.downgrade, "downgrade", false, "downgrade to the legacy yaml format")
f.BoolVar(&c.beforeAfter, "before-after", false, "print the befor and after")

f.StringVar(&c.org, "org", "default", "harness organization")
f.StringVar(&c.proj, "project", "default", "harness project")
f.StringVar(&c.name, "pipeline", "default", "harness pipeline name")
f.StringVar(&c.repoConn, "repo-connector", "", "repository connector")
f.StringVar(&c.repoName, "repo-name", "", "repository name")
f.StringVar(&c.kubeConn, "kube-connector", "", "kubernetes connector")
f.StringVar(&c.kubeName, "kube-namespace", "", "kubernets namespace")
f.StringVar(&c.dockerConn, "docker-connector", "", "dockerhub connector")
}

func (c *Rio) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
path := f.Arg(0)

fmt.Println("# v1 harness yaml is not supported for Rio pipelines. Converting to v0...")
// if the user does not specify the path as
// a command line arg, assume the default path.
if path == "" {
path = ".rio.yml"
}

// open the rio yaml
before, err := ioutil.ReadFile(path)
if err != nil {
log.Println(err)
return subcommands.ExitFailure
}

// convert the pipeline yaml from the rio
// format to the harness yaml format.
converter := rio.New(
rio.WithDockerhub(c.dockerConn),
rio.WithKubernetes(c.kubeName, c.kubeConn),
rio.WithName(c.name),
rio.WithIdentifier(c.name),
rio.WithOrganization(c.org),
rio.WithProject(c.proj),
)
after, err := converter.ConvertBytes(before)
if err != nil {
log.Println(err)
return subcommands.ExitFailure
}

// downgrade from the v1 harness yaml format
// to the v0 harness yaml format.
if c.downgrade {
fmt.Println("# downgrade for Rio pipeline is not supported")
//// downgrade to the v0 yaml
//d := downgrader.New(
// downgrader.WithCodebase(c.repoName, c.repoConn),
// downgrader.WithDockerhub(c.dockerConn),
// downgrader.WithKubernetes(c.kubeName, c.kubeName),
// downgrader.WithName(c.name),
// downgrader.WithOrganization(c.org),
// downgrader.WithProject(c.proj),
//)
//after, err = d.Downgrade(after)
//if err != nil {
// log.Println(err)
// return subcommands.ExitFailure
//}
}

if c.beforeAfter {
os.Stdout.WriteString("---\n")
os.Stdout.Write(before)
os.Stdout.WriteString("\n---\n")
}

os.Stdout.Write(after)

return subcommands.ExitSuccess
}
8 changes: 5 additions & 3 deletions convert/harness/yaml/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ type (
Spec *InfraSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
}

// InfraSpec describes pipeline infastructure.
// InfraSpec describes pipeline infrastructure.
InfraSpec struct {
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Conn string `json:"connectorRef,omitempty" yaml:"connectorRef,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Conn string `json:"connectorRef,omitempty" yaml:"connectorRef,omitempty"`
AutomountServiceToken bool `json:"automountServiceAccountToken,omitempty" yaml:"automountServiceAccountToken,omitempty"`
Os string `json:"os,omitempty" yaml:"os,omitempty"`
}

Platform struct {
Expand Down
2 changes: 1 addition & 1 deletion convert/harness/yaml/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type (
Reports []*Report `json:"reports,omitempty" yaml:"reports,omitempty"`
Resources *Resources `json:"resources,omitempty" yaml:"resources,omitempty"`
RunAsUser string `json:"runAsUser,omitempty" yaml:"runAsUser,omitempty"`
Tags map[string]string `json:"tags,omitempty" yaml:"tags,omitempty"`
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
Target string `json:"target,omitempty" yaml:"target,omitempty"`
}

Expand Down
244 changes: 244 additions & 0 deletions convert/rio/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
package rio

import (
"bytes"
"fmt"
"io"
"os"
"strings"

harness "github.com/drone/go-convert/convert/harness/yaml"
rio "github.com/drone/go-convert/convert/rio/yaml"
"github.com/drone/go-convert/internal/store"
"gopkg.in/yaml.v3"
)

// as we walk the yaml, we store a
// snapshot of the current node and
// its parents.
type context struct {
config *rio.Config
}

// Converter converts a Rio pipeline to a Harness
// v0 pipeline.
type Converter struct {
kubeEnabled bool
kubeNamespace string
kubeConnector string
kubeOs string
dockerhubConn string
identifiers *store.Identifiers

pipelineId string
pipelineName string
pipelineOrg string
pipelineProj string
}

// New creates a new Converter that converts a Travis
// pipeline to a Harness v1 pipeline.
func New(options ...Option) *Converter {
d := new(Converter)

// create the unique identifier store. this store
// is used for registering unique identifiers to
// prevent duplicate names, unique index violations.
d.identifiers = store.New()

// loop through and apply the options.
for _, option := range options {
option(d)
}

// set the default kubernetes namespace.
if d.kubeNamespace == "" {
d.kubeNamespace = "default"
}

// set default kubernetes OS
if d.kubeOs == "" {
d.kubeOs = "Linux"
}

// set the runtime to kubernetes if the kubernetes
// connector is configured.
if d.kubeConnector != "" {
d.kubeEnabled = true
}

// set default docker connector
if d.dockerhubConn == "" {
d.dockerhubConn = "account.harnessImage"
}

return d
}

// Convert converts a rio pipeline to v0.
func (d *Converter) Convert(r io.Reader) ([]byte, error) {
config, err := rio.Parse(r)
if err != nil {
return nil, err
}
return d.convert(&context{
config: config,
})
}

// ConvertBytes converts a rio pipeline to v0.
func (d *Converter) ConvertBytes(b []byte) ([]byte, error) {
return d.Convert(
bytes.NewBuffer(b),
)
}

// ConvertString converts a rio pipeline to v0.
func (d *Converter) ConvertString(s string) ([]byte, error) {
return d.Convert(
bytes.NewBufferString(s),
)
}

// ConvertFile converts a rio pipeline to v0.
func (d *Converter) ConvertFile(p string) ([]byte, error) {
f, err := os.Open(p)
if err != nil {
return nil, err
}
defer f.Close()
return d.Convert(f)
}

func (d *Converter) convertRunStep(p rio.Pipeline) harness.StepRun {
step := new(harness.StepRun)
step.Env = p.Machine.Env
command := ""
for _, s := range p.Build.Steps {
command += fmt.Sprintf("%s\n", s)
}
step.Command = command
step.Shell = "Sh"
step.ConnRef = d.dockerhubConn
step.Image = p.Machine.BaseImage
return *step
}

func (d *Converter) convertDockerSteps(dockerfile rio.Dockerfile) []harness.StepDocker {
steps := make([]harness.StepDocker, 0)
for _, r := range dockerfile.Publish {
step := new(harness.StepDocker)
step.Context = dockerfile.Context
step.Dockerfile = dockerfile.DockerfilePath
step.Repo = r.Repo
step.Tags = []string{dockerfile.Version}
step.BuildsArgs = dockerfile.Env
step.ConnectorRef = d.dockerhubConn
steps = append(steps, *step)
}
return steps
}

func (d *Converter) convertExecution(p rio.Pipeline) harness.Execution {
execution := harness.Execution{}

executionSteps := make([]*harness.Steps, 0)
// Append all commands in build attribute to one run step
if len(p.Build.Steps) != 0 {
steps := harness.Steps{
Step: &harness.Step{
Name: "run",
ID: "run",
Spec: d.convertRunStep(p),
Type: "Run",
},
}
executionSteps = append(executionSteps, &steps)
}

dockerStepCounter := 1
if len(p.Pkg.Dockerfile) != 0 {
if !p.Pkg.Release {
fmt.Println("# [WARN]: release=false is not supported")
}

// Each entry in package.Dockerfile attribute is one build and push step
for _, dockerfile := range p.Pkg.Dockerfile {
// each dockerfile entry can have multiple repos
dockerSteps := d.convertDockerSteps(dockerfile)

// Append all docker steps
for _, dStep := range dockerSteps {
steps := harness.Steps{
Step: &harness.Step{
Name: fmt.Sprintf("docker_build_and_push_%d", dockerStepCounter),
ID: fmt.Sprintf("docker_build_and_push_%d", dockerStepCounter),
Spec: &dStep,
Type: "BuildAndPushDockerRegistry",
},
}
executionSteps = append(executionSteps, &steps)
dockerStepCounter++
}
}
}
execution.Steps = executionSteps
return execution
}

func (d *Converter) convertCIStage(p rio.Pipeline) harness.StageCI {
stage := harness.StageCI{
Execution: d.convertExecution(p),
}
if d.kubeEnabled {
infra := harness.Infrastructure{
Type: "KubernetesDirect",
Spec: &harness.InfraSpec{
Namespace: d.kubeNamespace,
Conn: d.kubeConnector,
AutomountServiceToken: true,
Os: d.kubeOs,
},
}
stage.Infrastructure = &infra
}
return stage
}

func convertNameToID(name string) string {
ID := strings.ReplaceAll(name, " ", "_")
ID = strings.ReplaceAll(ID, "-", "_")
return ID
}

// converts converts a Travis pipeline to a Harness pipeline.
func (d *Converter) convert(ctx *context) ([]byte, error) {
// create the harness pipeline spec
pipeline := &harness.Pipeline{}
for _, p := range ctx.config.Pipelines {
stage := harness.Stages{
Stage: &harness.Stage{
Name: p.Name,
ID: convertNameToID(p.Name),
Spec: d.convertCIStage(p),
Type: "CI",
},
}
pipeline.Stages = append(pipeline.Stages, &stage)
}
pipeline.Name = d.pipelineName
pipeline.ID = d.pipelineId
pipeline.Org = d.pipelineOrg
pipeline.Project = d.pipelineProj

// create the harness pipeline resource
config := &harness.Config{Pipeline: *pipeline}

// marshal the harness yaml
out, err := yaml.Marshal(config)
if err != nil {
return nil, err
}

return out, nil
}
Loading

0 comments on commit cf3b744

Please sign in to comment.