Skip to content

Commit

Permalink
Add CircleCI
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhuGongpu committed Jul 22, 2020
1 parent 9e0b800 commit 9d26446
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 1 deletion.
142 changes: 142 additions & 0 deletions internal/cmdexport/orchestrators/circleci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2020 Google LLC
//
// 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 orchestrators

import (
"fmt"

"sigs.k8s.io/kustomize/kyaml/yaml"

"github.com/GoogleContainerTools/kpt/internal/cmdexport/types"
)

// CircleCI represents a config file for CircleCI pipelines.
type CircleCI struct {
Version string `yaml:",omitempty"`
Orbs map[string]*CircleCIOrb `yaml:",omitempty"`
Workflows map[string]*CircleCIWorkflow `yaml:",omitempty"`
}

func (p *CircleCI) Init(config *types.PipelineConfig) Pipeline {
p.Version = "2.1"

orbName := "kpt"
commandName := "kpt-fn-run"
jobName := "run-functions"
orbConfig := &CircleCIOrbConfig{
PipelineConfig: config,
ExecutorName: "kpt-container",
CommandName: commandName,
JobName: jobName,
}
orb := new(CircleCIOrb).Init(orbConfig)

p.Orbs = map[string]*CircleCIOrb{
orbName: orb,
}

p.Workflows = map[string]*CircleCIWorkflow{
"main": {
Jobs: []string{
fmt.Sprintf("%s/%s", orbName, jobName),
},
},
}

return p
}

func (p *CircleCI) Generate() (out []byte, err error) {
return yaml.Marshal(p)
}

// CircleCIOrb represents a reusable orb object that is a collection of executors, commands, and jobs.
type CircleCIOrb struct {
Executors map[string]*CircleCIExecutor `yaml:",omitempty"`
Commands map[string]*CircleCICommand `yaml:",omitempty"`
Jobs map[string]*CircleCIJob `yaml:",omitempty"`
}

// CircleCIOrbConfig allows to customize a CircleCI Orb object.
type CircleCIOrbConfig struct {
*types.PipelineConfig
ExecutorName string
CommandName string
JobName string
}

func (orb *CircleCIOrb) Init(config *CircleCIOrbConfig) *CircleCIOrb {
orb.Executors = map[string]*CircleCIExecutor{
config.ExecutorName: {
"docker": []*CircleCIDockerExecutor{
{Image: KptImage},
},
},
}

command := fmt.Sprintf("kpt fn run %s", config.Dir)
for _, fnPath := range config.FnPaths {
command = fmt.Sprintf("%s --fn-path %s", command, fnPath)
}

orb.Commands = map[string]*CircleCICommand{
config.CommandName: {
Steps: []*CircleCICommandStep{
{Run: command},
},
},
}

orb.Jobs = map[string]*CircleCIJob{
config.JobName: {
Executor: config.ExecutorName,
Steps: []string{
"setup_remote_docker",
config.CommandName,
},
},
}

return orb
}

// CircleCIExecutor represents an executor which only has one key as its type in the map.
type CircleCIExecutor = map[string][]*CircleCIDockerExecutor

// CircleCIDockerExecutor represents a dicker executor.
type CircleCIDockerExecutor struct {
Image string `yaml:",omitempty"`
}

// CircleCICommand represents a multi-step command.
type CircleCICommand struct {
Steps []*CircleCICommandStep `yaml:",omitempty"`
}

// CircleCICommandStep represents a step in the command steps.
type CircleCICommandStep struct {
Run string `yaml:",omitempty"`
}

// CircleCIJob wraps a sequence of commands to run and their executor.
type CircleCIJob struct {
Executor string `yaml:",omitempty"`
Steps []string `yaml:",omitempty"`
}

// CircleCIWorkflow defines a sequence of job to execute.
type CircleCIWorkflow struct {
Jobs []string `yaml:",omitempty"`
}
179 changes: 179 additions & 0 deletions internal/cmdexport/orchestrators/circleci_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Copyright 2020 Google LLC
//
// 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 orchestrators

import (
"strings"
"testing"

"gotest.tools/assert"
"sigs.k8s.io/kustomize/kyaml/yaml"

"github.com/GoogleContainerTools/kpt/internal/cmdexport/types"
"github.com/GoogleContainerTools/kpt/internal/testutil"
)

type circleCIOrbTestCase struct {
description string
config *CircleCIOrbConfig
expected string
}

var circleCIOrbTestCases = []circleCIOrbTestCase{
{
description: "generate a CircleCI Orb",
config: &CircleCIOrbConfig{
PipelineConfig: &types.PipelineConfig{
Dir: "resources",
FnPaths: nil,
},
ExecutorName: "kpt-container",
CommandName: "run-functions",
JobName: "run-kpt-functions",
},
expected: `
executors:
kpt-container:
docker:
- image: gongpu/kpt:latest
commands:
run-functions:
steps:
- run: kpt fn run resources
jobs:
run-kpt-functions:
executor: kpt-container
steps:
- setup_remote_docker
- run-functions
`,
},
{
description: "generate a CircleCI Orb with multiple fn-path",
config: &CircleCIOrbConfig{
PipelineConfig: &types.PipelineConfig{
Dir: "resources",
FnPaths: []string{
"config/gate-keeper.yaml",
"config/label-namespace.yaml",
},
},
ExecutorName: "kpt-container",
CommandName: "run-functions",
JobName: "run-kpt-functions",
},
expected: `
executors:
kpt-container:
docker:
- image: gongpu/kpt:latest
commands:
run-functions:
steps:
- run: kpt fn run resources --fn-path config/gate-keeper.yaml --fn-path
config/label-namespace.yaml
jobs:
run-kpt-functions:
executor: kpt-container
steps:
- setup_remote_docker
- run-functions
`,
},
}

func TestCircleCIOrb(t *testing.T) {
for i := range circleCIOrbTestCases {
testCase := circleCIOrbTestCases[i]

t.Run(testCase.description, func(t *testing.T) {
orb := new(CircleCIOrb).Init(testCase.config)

marshalledOrb, err := yaml.Marshal(orb)
testutil.AssertNoError(t, err)
actual := string(marshalledOrb)
expected := strings.TrimLeft(testCase.expected, "\n")

assert.Equal(t, expected, actual)
})
}
}

var circleCITestCases = []testCase{
{
description: "generate a CircleCI workflow",
config: &types.PipelineConfig{
Dir: "local-resources",
},
expected: `
version: "2.1"
orbs:
kpt:
executors:
kpt-container:
docker:
- image: gongpu/kpt:latest
commands:
kpt-fn-run:
steps:
- run: kpt fn run local-resources
jobs:
run-functions:
executor: kpt-container
steps:
- setup_remote_docker
- kpt-fn-run
workflows:
main:
jobs:
- kpt/run-functions
`,
},
{
description: "generate a CircleCI workflow with fn-path",
config: &types.PipelineConfig{
Dir: "local-resources",
FnPaths: []string{"functions.yaml"},
},
expected: `
version: "2.1"
orbs:
kpt:
executors:
kpt-container:
docker:
- image: gongpu/kpt:latest
commands:
kpt-fn-run:
steps:
- run: kpt fn run local-resources --fn-path functions.yaml
jobs:
run-functions:
executor: kpt-container
steps:
- setup_remote_docker
- kpt-fn-run
workflows:
main:
jobs:
- kpt/run-functions
`,
},
}

var circleCITestSuite = testSuite{
pipeline: new(CircleCI),
testCases: circleCITestCases,
}
1 change: 1 addition & 0 deletions internal/cmdexport/orchestrators/pipeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func TestPipeline(t *testing.T) {
gitlabCITestSuite,
jenkinsTestSuite,
tektonPipelineTestSuite,
circleCITestSuite,
}

for _, testSuite := range testSuites {
Expand Down
1 change: 0 additions & 1 deletion internal/cmdexport/orchestrators/tekton_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ func TestTektonTask(t *testing.T) {
}
}

// TODO: TektonPipeline
var tektonPipelineTestCases = []testCase{
{
description: "generate a tekton pipeline with a task",
Expand Down

0 comments on commit 9d26446

Please sign in to comment.