Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controlplane/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ config: check-buf-tool
.PHONY: api
# generate api proto bindings
api: check-buf-tool
cd ./plugins/sdk/v1/plugin/api && buf generate
cd ./api && buf generate

.PHONY: build
Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/cmd/wire_gen.go

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

30 changes: 24 additions & 6 deletions app/controlplane/internal/dispatcher/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ import (
type FanOutDispatcher struct {
integrationUC *biz.IntegrationUseCase
wfUC *biz.WorkflowUseCase
wfRunUC *biz.WorkflowRunUseCase
credentialsProvider credentials.ReaderWriter
casClient biz.CASClient
log *log.Helper
l log.Logger
loaded sdk.AvailablePlugins
}

func New(integrationUC *biz.IntegrationUseCase, wfUC *biz.WorkflowUseCase, creds credentials.ReaderWriter, c biz.CASClient, registered sdk.AvailablePlugins, l log.Logger) *FanOutDispatcher {
return &FanOutDispatcher{integrationUC, wfUC, creds, c, servicelogger.ScopedHelper(l, "fanout-dispatcher"), l, registered}
func New(integrationUC *biz.IntegrationUseCase, wfUC *biz.WorkflowUseCase, wfRunUC *biz.WorkflowRunUseCase, creds credentials.ReaderWriter, c biz.CASClient, registered sdk.AvailablePlugins, l log.Logger) *FanOutDispatcher {
return &FanOutDispatcher{integrationUC, wfUC, wfRunUC, creds, c, servicelogger.ScopedHelper(l, "fanout-dispatcher"), l, registered}
}

// Dispatch item is a plugin instance + resolved inputs that gets hydrated
Expand Down Expand Up @@ -101,11 +102,28 @@ func (d *FanOutDispatcher) Run(ctx context.Context, opts *RunOpts) error {
return fmt.Errorf("workflow not found")
}

wfRun, err := d.wfRunUC.View(ctx, opts.OrgID, opts.WorkflowRunID)
if err != nil {
return fmt.Errorf("finding workflow: %w", err)
} else if wfRun == nil {
return fmt.Errorf("workflowRun not found")
}

workflowMetadata := &sdk.ChainloopMetadata{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to increase the amount of metadata that we injected in the plugin. Including now information about the workflow run

WorkflowID: opts.WorkflowID,
WorkflowRunID: opts.WorkflowRunID,
WorkflowName: wf.Name,
WorkflowProject: wf.Project,
Workflow: &sdk.ChainloopMetadataWorkflow{
ID: opts.WorkflowID,
Name: wf.Name,
Project: wf.Project,
Team: wf.Team,
},
WorkflowRun: &sdk.ChainloopMetadataWorkflowRun{
ID: opts.WorkflowRunID,
State: wfRun.State,
StartedAt: *wfRun.CreatedAt,
FinishedAt: *wfRun.FinishedAt,
RunnerType: wfRun.RunnerType,
RunURL: wfRun.RunURL,
},
}

// Dispatch the integrations
Expand Down
2 changes: 1 addition & 1 deletion app/controlplane/internal/dispatcher/dispatcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (s *dispatcherTestSuite) SetupTest() {
l := log.NewStdLogger(io.Discard)

s.casClient = mocks.NewCASClient(s.T())
s.dispatcher = New(s.Integration, nil, nil, s.casClient, registeredIntegrations, l)
s.dispatcher = New(s.Integration, nil, nil, nil, s.casClient, registeredIntegrations, l)
}

type mockedIntegration struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (i *DependencyTrack) Execute(ctx context.Context, req *sdk.ExecutionRequest
"materialName", sbom.Name,
"host", registrationConfig.Domain,
"projectID", attachmentConfig.ProjectID, "projectName", attachmentConfig.ProjectName,
"workflowID", req.WorkflowID,
"workflowID", req.Workflow.ID,
)

// Create an SBOM client and perform validation and upload
Expand All @@ -195,7 +195,7 @@ func (i *DependencyTrack) Execute(ctx context.Context, req *sdk.ExecutionRequest
"materialName", sbom.Name,
"host", registrationConfig.Domain,
"projectID", attachmentConfig.ProjectID, "projectName", attachmentConfig.ProjectName,
"workflowID", req.WorkflowID,
"workflowID", req.Workflow.ID,
)
}

Expand Down
49 changes: 7 additions & 42 deletions app/controlplane/plugins/core/discord-webhook/v1/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import (
"io"
"mime/multipart"
"net/http"
"strings"
"text/template"

"github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1"
"github.com/go-kratos/kratos/v2/log"
Expand Down Expand Up @@ -142,22 +140,13 @@ func (i *Integration) Execute(_ context.Context, req *sdk.ExecutionRequest) erro
return fmt.Errorf("invalid registration config: %w", err)
}

attestationJSON, err := json.MarshalIndent(req.Input.Attestation.Statement, "", " ")
summary, err := sdk.SummaryTable(req)
if err != nil {
return fmt.Errorf("error marshaling JSON: %w", err)
}

metadata := req.ChainloopMetadata
tplData := &templateContent{
WorkflowID: metadata.WorkflowID,
WorkflowName: metadata.WorkflowName,
WorkflowRunID: metadata.WorkflowRunID,
WorkflowProject: metadata.WorkflowProject,
RunnerLink: req.Input.Attestation.Predicate.GetRunLink(),
return fmt.Errorf("generating summary table: %w", err)
}

webhookURL := req.RegistrationInfo.Credentials.Password
if err := executeWebhook(webhookURL, config.Username, attestationJSON, renderContent(tplData)); err != nil {
if err := executeWebhook(webhookURL, config.Username, []byte(summary), "New Attestation Received"); err != nil {
return fmt.Errorf("error executing webhook: %w", err)
}

Expand All @@ -183,7 +172,7 @@ func (i *Integration) Execute(_ context.Context, req *sdk.ExecutionRequest) erro
// --boundary
// Content-Disposition: form-data; name="files[0]"; filename="statement.json"
// --boundary
func executeWebhook(webhookURL, usernameOverride string, jsonStatement []byte, msgContent string) error {
func executeWebhook(webhookURL, usernameOverride string, statement []byte, msgContent string) error {
var b bytes.Buffer
multipartWriter := multipart.NewWriter(&b)

Expand All @@ -194,7 +183,7 @@ func executeWebhook(webhookURL, usernameOverride string, jsonStatement []byte, m
Attachments: []payloadAttachment{
{
ID: 0,
Filename: "attestation.json",
Filename: "statement.txt",
},
},
}
Expand All @@ -214,12 +203,12 @@ func executeWebhook(webhookURL, usernameOverride string, jsonStatement []byte, m
}

// attach attestation JSON
attachmentWriter, err := multipartWriter.CreateFormFile("files[0]", "statement.json")
attachmentWriter, err := multipartWriter.CreateFormFile("files[0]", "statement.txt")
if err != nil {
return fmt.Errorf("creating attachment form field: %w", err)
}

if _, err := attachmentWriter.Write(jsonStatement); err != nil {
if _, err := attachmentWriter.Write(statement); err != nil {
return fmt.Errorf("writing attachment form field: %w", err)
}

Expand Down Expand Up @@ -271,27 +260,3 @@ func validateExecuteRequest(req *sdk.ExecutionRequest) error {

return nil
}

type templateContent struct {
WorkflowID, WorkflowName, WorkflowProject, WorkflowRunID, RunnerLink string
}

func renderContent(metadata *templateContent) string {
t := template.Must(template.New("content").Parse(msgTemplate))

var b bytes.Buffer
if err := t.Execute(&b, metadata); err != nil {
return ""
}

return strings.Trim(b.String(), "\n")
}

const msgTemplate = `
New attestation received!
- Workflow: {{.WorkflowProject}}/{{.WorkflowName}}
- Workflow Run: {{.WorkflowRunID}}
{{- if .RunnerLink }}
- Link to runner: {{.RunnerLink}}
{{end}}
`
40 changes: 0 additions & 40 deletions app/controlplane/plugins/core/discord-webhook/v1/discord_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,46 +86,6 @@ func TestValidateRegistrationInput(t *testing.T) {
}
}

func TestRenderContent(t *testing.T) {
testCases := []struct {
name string
input *templateContent
expected string
}{
{
name: "all fields",
input: &templateContent{
WorkflowRunID: "deadbeef",
WorkflowName: "test",
WorkflowProject: "project",
RunnerLink: "http://runner.io",
},
expected: `New attestation received!
- Workflow: project/test
- Workflow Run: deadbeef
- Link to runner: http://runner.io`,
},
{
name: "no runner link",
input: &templateContent{
WorkflowRunID: "deadbeef",
WorkflowName: "test",
WorkflowProject: "project",
},
expected: `New attestation received!
- Workflow: project/test
- Workflow Run: deadbeef`,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual := renderContent(tc.input)
assert.Equal(t, tc.expected, actual)
})
}
}

func TestNewIntegration(t *testing.T) {
_, err := New(nil)
assert.NoError(t, err)
Expand Down
8 changes: 4 additions & 4 deletions app/controlplane/plugins/core/guac/v1/guac.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ func uploadToBucket(ctx context.Context, bucket *storage.BucketHandle, filename

w.ObjectAttrs.Metadata = map[string]string{
"author": "chainloop",
"workflowID": md.WorkflowID,
"workflowName": md.WorkflowName,
"workflowProject": md.WorkflowProject,
"workflowRunID": md.WorkflowRunID,
"workflowID": md.Workflow.ID,
"workflowName": md.Workflow.Name,
"workflowProject": md.Workflow.Project,
"workflowRunID": md.WorkflowRun.ID,
"filename": filename,
}

Expand Down
12 changes: 8 additions & 4 deletions app/controlplane/plugins/core/guac/v1/guac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ func (s *testSuite) TestUpload() {
var content = []byte("test")
const fileName = "sbom.json"
metadata := &sdk.ChainloopMetadata{
WorkflowID: "wid",
WorkflowRunID: "wid",
WorkflowName: "name",
WorkflowProject: "project",
Workflow: &sdk.ChainloopMetadataWorkflow{
ID: "wid",
Name: "name",
Project: "project",
},
WorkflowRun: &sdk.ChainloopMetadataWorkflowRun{
ID: "wid",
},
}

// Perform the upload
Expand Down
4 changes: 2 additions & 2 deletions app/controlplane/plugins/core/oci-registry/v1/ociregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (i *Integration) Execute(ctx context.Context, req *sdk.ExecutionRequest) er
return fmt.Errorf("creating OCI backend %w", err)
}

i.Logger.Infow("msg", "Uploading attestation", "repo", registrationConfig.Repository, "workflowID", req.WorkflowID)
i.Logger.Infow("msg", "Uploading attestation", "repo", registrationConfig.Repository, "workflowID", req.Workflow.ID)

// Perform the upload of the json marshalled attestation
jsonContent, err := json.Marshal(req.Input.Attestation.Envelope)
Expand All @@ -193,7 +193,7 @@ func (i *Integration) Execute(ctx context.Context, req *sdk.ExecutionRequest) er
return fmt.Errorf("uploading the attestation: %w", err)
}

i.Logger.Infow("msg", "Attestation uploaded", "repo", registrationConfig.Repository, "workflowID", req.WorkflowID)
i.Logger.Infow("msg", "Attestation uploaded", "repo", registrationConfig.Repository, "workflowID", req.Workflow.ID)

return nil
}
Expand Down
20 changes: 5 additions & 15 deletions app/controlplane/plugins/core/slack-webhook/v1/slack_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,15 @@ func (i *Integration) Execute(_ context.Context, req *sdk.ExecutionRequest) erro
return fmt.Errorf("running validation: %w", err)
}

attestationJSON, err := json.MarshalIndent(req.Input.Attestation.Statement, "", " ")
summary, err := sdk.SummaryTable(req)
if err != nil {
return fmt.Errorf("error marshaling JSON: %w", err)
}

metadata := req.ChainloopMetadata
// I was not able to make backticks work in the template
a := fmt.Sprintf("\n```\n%s\n```\n", string(attestationJSON))
tplData := &templateContent{
WorkflowID: metadata.WorkflowID,
WorkflowName: metadata.WorkflowName,
WorkflowRunID: metadata.WorkflowRunID,
WorkflowProject: metadata.WorkflowProject,
RunnerLink: req.Input.Attestation.Predicate.GetRunLink(),
Attestation: a,
return fmt.Errorf("error summarizing the request: %w", err)
}

msg := fmt.Sprintf("\nNew attestation received!```\n%s\n```\n", summary)
webhookURL := req.RegistrationInfo.Credentials.Password
if err := executeWebhook(webhookURL, renderContent(tplData)); err != nil {

if err := executeWebhook(webhookURL, msg); err != nil {
return fmt.Errorf("error executing webhook: %w", err)
}

Expand Down
23 changes: 6 additions & 17 deletions app/controlplane/plugins/core/smtp/v1/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package smtp

import (
"context"
"encoding/json"
"errors"
"fmt"
nsmtp "net/smtp"
Expand Down Expand Up @@ -145,7 +144,7 @@ func (i *Integration) Execute(_ context.Context, req *sdk.ExecutionRequest) erro
i.Logger.Info("execution requested")

if err := validateExecuteRequest(req); err != nil {
return fmt.Errorf("running validation for workflow id %s: %w", req.WorkflowID, err)
return fmt.Errorf("running validation for workflow id %s: %w", req.Workflow.ID, err)
}

var rc *registrationState
Expand All @@ -158,27 +157,16 @@ func (i *Integration) Execute(_ context.Context, req *sdk.ExecutionRequest) erro
return errors.New("invalid attachment configuration")
}

// marshal the statement
jsonBytes, err := json.MarshalIndent(req.Input.Attestation.Statement, "", " ")
summary, err := sdk.SummaryTable(req)
if err != nil {
return fmt.Errorf("error marshaling JSON: %w", err)
return fmt.Errorf("generating summary table: %w", err)
}

// send the email
to, from, user, password, host, port := rc.To, rc.From, rc.User, req.RegistrationInfo.Credentials.Password, rc.Host, rc.Port
subject := "[chainloop] New workflow run finished successfully!"
tpl := `A new workflow run finished successfully!

# Workflow: %s

# in-toto statement:
%s

This email has been delivered via integration %s version %s.
`
body := fmt.Sprintf(tpl, req.WorkflowID, jsonBytes, i.Describe().ID, i.Describe().Version)
err = sendEmail(host, port, user, password, from, to, ac.CC, subject, body)
if err != nil {
if err := sendEmail(host, port, user, password, from, to, ac.CC, subject,
"<pre>"+summary+"</pre>"); err != nil {
return fmt.Errorf("sending an email: %w", err)
}

Expand Down Expand Up @@ -209,6 +197,7 @@ func sendEmail(host string, port string, user, password, from, to, cc, subject,
message := "From: " + from + "\n" +
"To: " + to + "\n" +
"CC: " + cc + "\n" +
"MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n" +
"Subject: " + subject + "\n\n" +
body

Expand Down
Loading