Skip to content
This repository has been archived by the owner on Jan 19, 2024. It is now read-only.

feat: Utilize gitCommitID from cloud events to fetch resources #303

Merged
merged 9 commits into from
Jul 8, 2022
14 changes: 12 additions & 2 deletions cmd/job-executor-service-initcontainer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"time"

"github.com/kelseyhightower/envconfig"
api "github.com/keptn/go-utils/pkg/api/utils"
api "github.com/keptn/go-utils/pkg/api/utils/v2"
"github.com/spf13/afero"
)

Expand Down Expand Up @@ -49,6 +49,8 @@ type envConfig struct {
OAuthScopes []string `envconfig:"OAUTH_SCOPES" required:"false"`
// The well known oauth discovery url for the init container
OAuthDiscovery string `envconfig:"OAUTH_DISCOVERY" required:"false"`
// The gitCommitId of the initial cloud event, for older Keptn instances this might be empty
GitCommitID string `envconfig:"GIT_COMMIT_ID"`
}

func main() {
Expand Down Expand Up @@ -118,7 +120,15 @@ func main() {
useLocalFileSystem = true
}

configService := keptn.NewConfigService(useLocalFileSystem, env.Project, env.Stage, env.Service, keptnAPI.ResourcesV1())
// re-create the event from job-executor-service
eventProps := keptn.EventProperties{
Project: env.Project,
Stage: env.Stage,
Service: env.Service,
GitCommitID: env.GitCommitID,
}

configService := keptn.NewConfigService(useLocalFileSystem, eventProps, keptnAPI.Resources())

err = file.MountFiles(env.Action, env.Task, fs, configService)
if err != nil {
Expand Down
10 changes: 6 additions & 4 deletions cmd/job-executor-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@ func processKeptnCloudEvent(ctx context.Context, event cloudevents.Event, allowL
// create a uniform handler talking to the distributor
uniformHandler := api.NewUniformHandler("localhost:8081/controlPlane")
var eventHandler = &eventhandler.EventHandler{
Keptn: myKeptn,
JobConfigReader: &config.JobConfigReader{Keptn: myKeptn},
ServiceName: ServiceName,
Mapper: new(eventhandler.KeptnCloudEventMapper),
Keptn: myKeptn,
JobConfigReader: &config.JobConfigReader{
Keptn: keptn_interface.NewV1ResourceHandler(myKeptn.Event, myKeptn.ResourceHandler),
},
ServiceName: ServiceName,
Mapper: new(eventhandler.KeptnCloudEventMapper),
ImageFilter: imageFilterImpl{
imageFilterList: allowList,
},
Expand Down
12 changes: 6 additions & 6 deletions pkg/config/fake/reader_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions pkg/config/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const jobConfigResourceName = "job/config.yaml"
// KeptnResourceService defines the contract used by JobConfigReader to retrieve a resource from keptn (using project,
// service, stage from context)
type KeptnResourceService interface {
GetKeptnResource(resource string) ([]byte, error)
GetResource(resource string, gitCommitID string) ([]byte, error)
}

// JobConfigReader retrieves and parses job configuration from Keptn
Expand All @@ -25,8 +25,9 @@ type JobConfigReader struct {
// Additionally, also the SHA1 hash of the retrieved configuration will be returned.
// In case of error retrieving the resource or parsing the yaml it will return (nil,
// error) with the original error correctly wrapped in the local one
func (jcr *JobConfigReader) GetJobConfig() (*Config, string, error) {
resource, err := jcr.Keptn.GetKeptnResource(jobConfigResourceName)
func (jcr *JobConfigReader) GetJobConfig(gitCommitID string) (*Config, string, error) {

resource, err := jcr.Keptn.GetResource(jobConfigResourceName, gitCommitID)
if err != nil {
return nil, "", fmt.Errorf("error retrieving job config: %w", err)
}
Expand All @@ -42,5 +43,6 @@ func (jcr *JobConfigReader) GetJobConfig() (*Config, string, error) {
log.Printf("The config was: %s", string(resource))
return nil, "", fmt.Errorf("error parsing job configuration: %w", err)
}

return configuration, resourceHash, nil
}
12 changes: 6 additions & 6 deletions pkg/config/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ func TestConfigRetrievalFailed(t *testing.T) {

mockKeptnResourceService := fake.NewMockKeptnResourceService(mockCtrl)
retrievalError := errors.New("error getting resource")
mockKeptnResourceService.EXPECT().GetKeptnResource("job/config.yaml").Return(nil, retrievalError)
mockKeptnResourceService.EXPECT().GetResource("job/config.yaml", "c25692cb4fe4068fbdc2").Return(nil, retrievalError)

sut := JobConfigReader{Keptn: mockKeptnResourceService}

config, _, err := sut.GetJobConfig()
config, _, err := sut.GetJobConfig("c25692cb4fe4068fbdc2")
assert.ErrorIs(t, err, retrievalError)
assert.Nil(t, config)
}
Expand All @@ -35,14 +35,14 @@ func TestMalformedConfig(t *testing.T) {
has_nothing_to_do:
with_job_executor: true
`
mockKeptnResourceService.EXPECT().GetKeptnResource("job/config.yaml").Return(
mockKeptnResourceService.EXPECT().GetResource("job/config.yaml", "").Return(
[]byte(yamlConfig),
nil,
)

sut := JobConfigReader{Keptn: mockKeptnResourceService}

config, _, err := sut.GetJobConfig()
config, _, err := sut.GetJobConfig("")
assert.Error(t, err)
assert.Nil(t, config)
}
Expand All @@ -64,14 +64,14 @@ func TestGetConfigHappyPath(t *testing.T) {
cmd:
- echo "Hello World!"
`
mockKeptnResourceService.EXPECT().GetKeptnResource("job/config.yaml").Return(
mockKeptnResourceService.EXPECT().GetResource("job/config.yaml", "c25692cb4fe4068fbdc2").Return(
[]byte(yamlConfig),
nil,
)

sut := JobConfigReader{Keptn: mockKeptnResourceService}

config, _, err := sut.GetJobConfig()
config, _, err := sut.GetJobConfig("c25692cb4fe4068fbdc2")
assert.NoError(t, err)
assert.NotNil(t, config)
}
17 changes: 13 additions & 4 deletions pkg/eventhandler/eventhandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type EventMapper interface {

// JobConfigReader retrieves the job-executor-service configuration
type JobConfigReader interface {
GetJobConfig() (*config.Config, string, error)
GetJobConfig(gitCommitID string) (*config.Config, string, error)
}

// ErrorLogSender is used to send error logs that will appear in Uniform UI
Expand Down Expand Up @@ -97,7 +97,13 @@ func (eh *EventHandler) HandleEvent() error {
)
log.Printf("CloudEvent %T: %v", eventAsInterface, eventAsInterface)

configuration, configHash, err := eh.JobConfigReader.GetJobConfig()
// Get the git commit id from the cloud event (if it exists) and use it to query the job configuration
var gitCommitID string
if commitId, ok := eventAsInterface["gitcommitid"]; ok {
Raffy23 marked this conversation as resolved.
Show resolved Hide resolved
gitCommitID, _ = commitId.(string)
}

configuration, configHash, err := eh.JobConfigReader.GetJobConfig(gitCommitID)

if err != nil {
errorLogErr := eh.ErrorSender.SendErrorLogEvent(
Expand Down Expand Up @@ -134,14 +140,16 @@ func (eh *EventHandler) HandleEvent() error {
eh.Keptn.CloudEvent.Type(), action.Name,
)

eh.startK8sJob(&action, actionIndex, configHash, eventAsInterface)
eh.startK8sJob(&action, actionIndex, configHash, gitCommitID, eventAsInterface)
}
}

return nil
}

func (eh *EventHandler) startK8sJob(action *config.Action, actionIndex int, configHash string, jsonEventData interface{}) {
func (eh *EventHandler) startK8sJob(action *config.Action, actionIndex int, configHash string, gitCommitID string,
jsonEventData interface{},
) {

if !action.Silent {
_, err := eh.Keptn.SendTaskStartedEvent(nil, eh.ServiceName)
Expand Down Expand Up @@ -199,6 +207,7 @@ func (eh *EventHandler) startK8sJob(action *config.Action, actionIndex int, conf
ActionIndex: actionIndex,
TaskIndex: index,
JobConfigHash: configHash,
GitCommitID: gitCommitID,
}

err = eh.K8s.CreateK8sJob(
Expand Down
14 changes: 7 additions & 7 deletions pkg/eventhandler/eventhandlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func TestErrorGettingJobConfig(t *testing.T) {
mockUniformErrorSender := eventhandlerfake.NewMockErrorLogSender(mockCtrl)

errorGettingJobConfig := errors.New("error getting resource")
mockJobConfigReader.EXPECT().GetJobConfig().Return(
mockJobConfigReader.EXPECT().GetJobConfig("").Return(
nil, "",
errorGettingJobConfig,
).Times(1)
Expand Down Expand Up @@ -211,7 +211,7 @@ func TestErrorConnectingToK8s(t *testing.T) {
mockJobConfigReader := eventhandlerfake.NewMockJobConfigReader(mockCtrl)
mockUniformErrorSender := eventhandlerfake.NewMockErrorLogSender(mockCtrl)

mockJobConfigReader.EXPECT().GetJobConfig().Return(
mockJobConfigReader.EXPECT().GetJobConfig("").Return(
&config.Config{
APIVersion: &apiVersion,
Actions: []config.Action{
Expand Down Expand Up @@ -375,7 +375,7 @@ func TestEventMatching(t *testing.T) {
}

mockJobConfigReader := eventhandlerfake.NewMockJobConfigReader(mockCtrl)
mockJobConfigReader.EXPECT().GetJobConfig().Return(
mockJobConfigReader.EXPECT().GetJobConfig("").Return(
&config, "<config-hash-value>",
nil,
).Times(1)
Expand Down Expand Up @@ -510,7 +510,7 @@ func TestStartK8s(t *testing.T) {
k8sMock.EXPECT().GetLogsOfPod(gomock.Eq(jobName1), jobNamespace1).Times(1)
k8sMock.EXPECT().GetLogsOfPod(gomock.Eq(jobName2), jobNamespace2).Times(1)

eh.startK8sJob(&action, 0, "", eventPayloadAsInterface)
eh.startK8sJob(&action, 0, "", "", eventPayloadAsInterface)

err = fakeEventSender.AssertSentEventTypes(
[]string{
Expand Down Expand Up @@ -561,7 +561,7 @@ func TestStartK8sJobSilent(t *testing.T) {
k8sMock.EXPECT().GetLogsOfPod(gomock.Eq(jobName1), gomock.Any()).Times(1)
k8sMock.EXPECT().GetLogsOfPod(gomock.Eq(jobName2), gomock.Any()).Times(1)

eh.startK8sJob(&action, 0, "", eventPayloadAsInterface)
eh.startK8sJob(&action, 0, "", "", eventPayloadAsInterface)

err = fakeEventSender.AssertSentEventTypes([]string{})
assert.NoError(t, err)
Expand Down Expand Up @@ -605,7 +605,7 @@ func TestStartK8s_TestFinishedEvent(t *testing.T) {
require.NoError(t, err)
time.Local = local

eh.startK8sJob(&action, 0, "", eventPayloadAsInterface)
eh.startK8sJob(&action, 0, "", "", eventPayloadAsInterface)

err = fakeEventSender.AssertSentEventTypes(
[]string{
Expand Down Expand Up @@ -677,7 +677,7 @@ func TestExpectImageNotAllowedError(t *testing.T) {
require.NoError(t, err)
time.Local = local

eh.startK8sJob(&action, 0, "", eventPayloadAsInterface)
eh.startK8sJob(&action, 0, "", "", eventPayloadAsInterface)

err = fakeEventSender.AssertSentEventTypes(
[]string{
Expand Down
3 changes: 3 additions & 0 deletions pkg/eventhandler/eventmapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ func (kcem *KeptnCloudEventMapper) Map(ce cloudevents.Event) (map[string]interfa
extension, _ := ce.Context.GetExtension("shkeptncontext")
shKeptnContext := extension.(string)

gitCommitID, _ := ce.Context.GetExtension("gitcommitid")

eventAsInterface := make(map[string]interface{})
eventAsInterface["id"] = ce.ID()
eventAsInterface["shkeptncontext"] = shKeptnContext
Expand All @@ -34,6 +36,7 @@ func (kcem *KeptnCloudEventMapper) Map(ce cloudevents.Event) (map[string]interfa
eventAsInterface["data"] = eventDataAsInterface
eventAsInterface["specversion"] = ce.SpecVersion()
eventAsInterface["type"] = ce.Type()
eventAsInterface["gitcommitid"] = gitCommitID

return eventAsInterface, nil
}
8 changes: 4 additions & 4 deletions pkg/eventhandler/fake/eventhandlers_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func TestMountFilesFileNotFound(t *testing.T) {
func TestMountFilesWithLocalFileSystem(t *testing.T) {

fs := afero.NewMemMapFs()
configService := keptn.NewConfigService(true, "", "", "", nil)
configService := keptn.NewConfigService(true, keptn.EventProperties{}, nil)
err := afero.WriteFile(fs, "job/config.yaml", []byte(simpleConfig), 0644)
assert.NoError(t, err)
err = afero.WriteFile(fs, "/helm/values.yaml", []byte("here be awesome configuration"), 0644)
Expand Down
13 changes: 6 additions & 7 deletions pkg/k8sutils/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type JobDetails struct {
ActionIndex int
TaskIndex int
JobConfigHash string
GitCommitID string
}

// JobSettings contains environment variable settings for the job
Expand Down Expand Up @@ -323,6 +324,10 @@ func (k8s *K8sImpl) CreateK8sJob(
Name: "JOB_TASK",
Value: task.Name,
},
{
Name: "GIT_COMMIT_ID",
Value: jobDetails.GitCommitID,
},
},
Resources: *jobSettings.DefaultResourceRequirements,
},
Expand Down Expand Up @@ -614,12 +619,6 @@ func generateK8sJobLabels(jobDetails JobDetails, jsonEventData interface{}, jesD
return nil, fmt.Errorf("jsonEventData does not contain the field id")
}

gitCommitID, ok := eventAsMap["gitcommitid"].(string)
if !ok {
// For legacy events that have no git commit id we just set it to an empty string
gitCommitID = ""
}

// This function is used to sanitize the labels for the action and the task name to
// avoid creating a set of labels that is not allowed by kubernetes
sanitizeLabel := func(label string) string {
Expand All @@ -642,7 +641,7 @@ func generateK8sJobLabels(jobDetails JobDetails, jsonEventData interface{}, jesD
"app.kubernetes.io/managed-by": jesDeploymentName,
"keptn.sh/context": keptnContext,
"keptn.sh/event-id": eventID,
"keptn.sh/commitid": gitCommitID,
"keptn.sh/commitid": jobDetails.GitCommitID,
"keptn.sh/jes-action": sanitizeLabel(jobDetails.Action.Name),
"keptn.sh/jes-task": sanitizeLabel(jobDetails.Task.Name),
"keptn.sh/jes-job-confighash": jobDetails.JobConfigHash,
Expand Down
12 changes: 7 additions & 5 deletions pkg/k8sutils/job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,11 @@ func TestCreateK8sJobContainsCorrectLabels(t *testing.T) {
t.Run(test.name, func(t *testing.T) {

jobName := "some-job-name-" + strconv.Itoa(i)
var gitCommitID string
if id, ok := test.event["gitcommitid"]; ok {
gitCommitID = id.(string)
}

err = k8s.CreateK8sJob(
jobName,
JobDetails{
Expand All @@ -1141,6 +1146,7 @@ func TestCreateK8sJobContainsCorrectLabels(t *testing.T) {
ActionIndex: 0,
TaskIndex: 0,
JobConfigHash: "",
GitCommitID: gitCommitID,
},
&eventData,
JobSettings{
Expand All @@ -1165,18 +1171,14 @@ func TestCreateK8sJobContainsCorrectLabels(t *testing.T) {
"app.kubernetes.io/managed-by": "job-executor-service",
"keptn.sh/context": test.event["shkeptncontext"].(string),
"keptn.sh/event-id": test.event["id"].(string),
"keptn.sh/commitid": "",
"keptn.sh/commitid": gitCommitID,
"keptn.sh/jes-action": test.expectedActionName,
"keptn.sh/jes-task": test.expectedTaskName,
"keptn.sh/jes-job-confighash": "",
"keptn.sh/jes-action-index": "0",
"keptn.sh/jes-task-index": "0",
}

if test.event["gitcommitid"] != nil {
expectedLabels["keptn.sh/commitid"] = test.event["gitcommitid"].(string)
}

assert.Equal(t, expectedLabels, job.Labels)
})
}
Expand Down
Loading