-
Notifications
You must be signed in to change notification settings - Fork 12
[WIP] Feature/poc GitHub actions #69
base: main
Are you sure you want to change the base?
Changes from 13 commits
6520485
870b804
da06129
e9b7053
a55eecb
c1fe76e
94e2551
07d66de
06d6b02
dda660a
afc7c98
ef1a4f7
e6377a8
b746361
47d7745
9587c8f
579cf79
5148d60
fca8bab
80116b3
00c75bc
b249f3f
2894faf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
creationTimestamp: null | ||
labels: | ||
app: registry | ||
name: registry | ||
spec: | ||
replicas: 1 | ||
selector: | ||
matchLabels: | ||
app: registry | ||
strategy: {} | ||
template: | ||
metadata: | ||
creationTimestamp: null | ||
labels: | ||
app: registry | ||
spec: | ||
containers: | ||
- image: registry:2 | ||
name: registry | ||
ports: | ||
- containerPort: 5000 | ||
resources: {} | ||
status: {} | ||
|
||
--- | ||
apiVersion: v1 | ||
kind: Service | ||
metadata: | ||
creationTimestamp: null | ||
labels: | ||
app: registry | ||
name: registry | ||
spec: | ||
ports: | ||
- port: 5000 | ||
protocol: TCP | ||
targetPort: 5000 | ||
selector: | ||
app: registry | ||
status: | ||
loadBalancer: {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,12 @@ import ( | |
"encoding/json" | ||
"fmt" | ||
"keptn-sandbox/job-executor-service/pkg/config" | ||
"keptn-sandbox/job-executor-service/pkg/github" | ||
"keptn-sandbox/job-executor-service/pkg/k8sutils" | ||
"log" | ||
"math" | ||
"strconv" | ||
"strings" | ||
|
||
cloudevents "github.com/cloudevents/sdk-go/v2" // make sure to use v2 cloudevents here | ||
keptnv2 "github.com/keptn/go-utils/pkg/lib/v0_2_0" | ||
|
@@ -75,11 +77,86 @@ func (eh *EventHandler) HandleEvent() error { | |
log.Printf("Match found for event %s of type %s. Starting k8s job to run action '%s'", eh.Event.Context.GetID(), eh.Event.Type(), action.Name) | ||
|
||
k8s := k8sutils.NewK8s(eh.JobSettings.JobNamespace) | ||
err = eh.handleGithubAction(k8s, configuration) | ||
if err != nil { | ||
log.Printf("An error occurred while handling GitHub action: %s", err) | ||
return err | ||
} | ||
|
||
eh.startK8sJob(k8s, action, eventAsInterface) | ||
|
||
return nil | ||
} | ||
|
||
func (eh *EventHandler) handleGithubAction(k8s k8sutils.K8s, configuration *config.Config) error { | ||
|
||
_, err := eh.Keptn.SendTaskStartedEvent(eh.EventData, eh.ServiceName) | ||
if err != nil { | ||
return fmt.Errorf("Error while sending started event: %s\n", err.Error()) | ||
} | ||
|
||
err = k8s.ConnectToCluster() | ||
if err != nil { | ||
return fmt.Errorf("Error while connecting to cluster: %s\n", err.Error()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [golint] reported by reviewdog 🐶 |
||
sendTaskFailedEvent(eh.Keptn, "", eh.ServiceName, err, "") | ||
} | ||
|
||
for _, action := range configuration.Actions { | ||
for _, step := range action.Steps { | ||
githubProjectName := eh.getGithubProjectName(step.Uses) | ||
|
||
// TODO call the functionality from Thomas (build and push image) | ||
imageLocation, err := k8s.CreateImageBuilder(step.Name, step, eh.JobSettings.ContainerRegistry) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer func() { | ||
err = k8s.DeleteK8sJob(step.Name) | ||
if err != nil { | ||
log.Printf("Error while deleting job: %s\n", err.Error()) | ||
} | ||
}() | ||
|
||
jobErr := k8s.AwaitK8sJobDone(step.Name, defaultMaxPollCount, pollIntervalInSeconds) | ||
if jobErr != nil { | ||
log.Println(err) | ||
} | ||
|
||
err, githubAction := github.GetActionYaml(githubProjectName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
args, err := github.PrepareArgs(step.With, githubAction.Inputs, githubAction.Runs.Args) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
task := config.Task{ | ||
Name: githubAction.Name, | ||
Files: nil, | ||
Image: imageLocation, | ||
Cmd: nil, | ||
Args: args, | ||
Env: nil, | ||
} | ||
|
||
action.Tasks = append(action.Tasks, task) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (eh *EventHandler) getGithubProjectName(uses string) string { | ||
githubProjectName := uses | ||
index := strings.LastIndex(githubProjectName, "@") | ||
if index > 0 { | ||
githubProjectName = githubProjectName[:index] | ||
} | ||
return githubProjectName | ||
} | ||
|
||
func (eh *EventHandler) createEventPayloadAsInterface() (map[string]interface{}, error) { | ||
|
||
var eventDataAsInterface interface{} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package github | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [gofmt] reported by reviewdog 🐶 |
||
|
||
import ( | ||
"fmt" | ||
"keptn-sandbox/job-executor-service/pkg/github/model" | ||
"log" | ||
"strings" | ||
) | ||
|
||
func PrepareArgs(with map[string]string, inputs map[string]model.Input, args []string) ([]string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [golint] reported by reviewdog 🐶 |
||
var filledArgs []string | ||
|
||
for inputKey, inputValue := range inputs { | ||
argKey := fmt.Sprintf("inputs.%s", inputKey) | ||
log.Printf("argKey: %v", argKey) | ||
|
||
for _, arg := range args { | ||
if strings.Contains(arg, argKey) { | ||
log.Printf("matched argKey: %v", argKey) | ||
|
||
argValue := inputValue.Default | ||
if withValue, ok := with[inputKey]; ok { | ||
argValue = withValue | ||
} else { | ||
if inputValue.Required { | ||
return nil, fmt.Errorf("required input '%s' not provided", inputKey) | ||
} | ||
} | ||
|
||
splittedArg := strings.Split(arg, "$") | ||
arg := strings.TrimSpace(splittedArg[0]) | ||
filledArgs = append(filledArgs, arg) | ||
filledArgs = append(filledArgs, argValue) | ||
} | ||
} | ||
} | ||
|
||
return filledArgs, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package github | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [gofmt] reported by reviewdog 🐶 |
||
|
||
import ( | ||
"gotest.tools/assert" | ||
"keptn-sandbox/job-executor-service/pkg/github/model" | ||
"log" | ||
"testing" | ||
) | ||
|
||
func TestPrepareArgs(t *testing.T) { | ||
with := map[string]string{"scan-type": "banana", "format": "cucumber"} | ||
inputs := map[string]model.Input{ | ||
"scan-type": { | ||
Required: false, | ||
Default: "", | ||
}, | ||
"format": { | ||
Required: false, | ||
Default: "", | ||
}, | ||
"template": { | ||
Required: false, | ||
Default: "table", | ||
}, | ||
} | ||
args := []string{"-a ${{ inputs.scan-type }}", "-b ${{ inputs.format }}", "-c ${{ inputs.template }}"} | ||
k8sArgs, err := PrepareArgs(with, inputs, args) | ||
assert.NilError(t, err) | ||
log.Printf("%v", k8sArgs) | ||
} | ||
|
||
func TestPrepareArgs_RequiredInput(t *testing.T) { | ||
with := map[string]string{} | ||
inputs := map[string]model.Input{ | ||
"scan-type": { | ||
Required: true, | ||
Default: "", | ||
}, | ||
} | ||
args := []string{"-a ${{ inputs.scan-type }}"} | ||
_, err := PrepareArgs(with, inputs, args) | ||
assert.Error(t, err, "required input 'scan-type' not provided") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package model | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [gofmt] reported by reviewdog 🐶 |
||
|
||
import ( | ||
"gopkg.in/yaml.v3" | ||
"io" | ||
) | ||
|
||
// ActionRunsUsing is the type of runner for the action | ||
type ActionRunsUsing string | ||
|
||
const ( | ||
// ActionRunsUsingNode12 for running with node12 | ||
ActionRunsUsingNode12 = "node12" | ||
// ActionRunsUsingDocker for running with docker | ||
ActionRunsUsingDocker = "docker" | ||
// ActionRunsUsingComposite for running composite | ||
ActionRunsUsingComposite = "composite" | ||
) | ||
|
||
// ActionRuns are a field in Action | ||
type ActionRuns struct { | ||
Using ActionRunsUsing `yaml:"using"` | ||
Env map[string]string `yaml:"env"` | ||
Main string `yaml:"main"` | ||
Image string `yaml:"image"` | ||
Entrypoint []string `yaml:"entrypoint"` | ||
Args []string `yaml:"args"` | ||
Steps []Step `yaml:"steps"` | ||
} | ||
|
||
// Action describes a metadata file for GitHub actions. The metadata filename must be either action.yml or action.yaml. The data in the metadata file defines the inputs, outputs and main entrypoint for your action. | ||
type Action struct { | ||
Name string `yaml:"name"` | ||
Author string `yaml:"author"` | ||
Description string `yaml:"description"` | ||
Inputs map[string]Input `yaml:"inputs"` | ||
Outputs map[string]Output `yaml:"outputs"` | ||
Runs ActionRuns `yaml:"runs"` | ||
Branding struct { | ||
Color string `yaml:"color"` | ||
Icon string `yaml:"icon"` | ||
} `yaml:"branding"` | ||
} | ||
|
||
// Input parameters allow you to specify data that the action expects to use during runtime. GitHub stores input parameters as environment variables. Input ids with uppercase letters are converted to lowercase during runtime. We recommended using lowercase input ids. | ||
type Input struct { | ||
Description string `yaml:"description"` | ||
Required bool `yaml:"required"` | ||
Default string `yaml:"default"` | ||
} | ||
|
||
// Output parameters allow you to declare data that an action sets. Actions that run later in a workflow can use the output data set in previously run actions. For example, if you had an action that performed the addition of two inputs (x + y = z), the action could output the sum (z) for other actions to use as an input. | ||
type Output struct { | ||
Description string `yaml:"description"` | ||
Value string `yaml:"value"` | ||
} | ||
|
||
// ReadAction reads an action from a reader | ||
func ReadAction(in io.Reader) (*Action, error) { | ||
a := new(Action) | ||
err := yaml.NewDecoder(in).Decode(a) | ||
return a, err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[golint] reported by reviewdog 🐶
error strings should not be capitalized or end with punctuation or a newline