diff --git a/Makefile b/Makefile index d177ec5d2..c9c2de33f 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,6 @@ PG_VERSION=11.9.8 DRACON_NS=dracon TEKTON_NS=tekton-pipelines ARANGODB_NS=arangodb -BASE_IMAGE=scratch DOCKER=docker PROTOC=protoc @@ -47,22 +46,31 @@ export .PHONY: components component-binaries cmd/draconctl/bin protos build publish-component-containers publish-containers draconctl-image draconctl-image-publish clean-protos clean $(component_binaries): - CGO_ENABLED=0 ./scripts/build_component_binary.sh $@ + ./scripts/build_component_binary.sh $@ component-binaries: $(component_binaries) $(component_containers): %/docker: %/bin + $(eval GOOS:=linux) + $(eval GOARCH:=amd64) ./scripts/build_component_container.sh $@ components: $(component_containers) cmd/draconctl/bin: - CGO_ENABLED=0 go build -o bin/cmd/draconctl cmd/draconctl/main.go + $(eval GOOS:=linux) + $(eval GOARCH:=amd64) + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o bin/cmd/$(GOOS)/$(GOARCH)/draconctl cmd/draconctl/main.go draconctl-image: cmd/draconctl/bin + $(eval GOOS:=linux) + $(eval GOARCH:=amd64) $(DOCKER) build -t "${CONTAINER_REPO}/draconctl:${DRACON_VERSION}" \ + --build-arg GOOS=$(GOOS) \ + --build-arg GOARCH=$(GOARCH) \ $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - -f containers/Dockerfile.draconctl . + -f containers/Dockerfile.draconctl . \ + --platform "$(GOOS)/$(GOARCH)" draconctl-image-publish: draconctl-image $(DOCKER) push "${CONTAINER_REPO}/draconctl:${DRACON_VERSION}" @@ -229,23 +237,23 @@ install: deploy-cluster dev-infra deploy-elasticoperator deploy-arangodb-crds ad @echo "deploying dracon" @helm upgrade dracon ./deploy/dracon/chart \ - --install \ - --values ./deploy/dracon/values/dev.yaml \ - --create-namespace \ - --set "image.registry=$(CONTAINER_REPO)" \ - --namespace $(DRACON_NS) \ - --version $(DRACON_VERSION) \ - --wait + --install \ + --values ./deploy/dracon/values/dev.yaml \ + --create-namespace \ + --set "image.registry=$(CONTAINER_REPO)" \ + --namespace $(DRACON_NS) \ + --version $(DRACON_VERSION) \ + --wait @echo "Applying migrations" @helm upgrade deduplication-db-migrations ./deploy/deduplication-db-migrations/chart \ - --install \ - --values ./deploy/deduplication-db-migrations/values/dev.yaml \ - --create-namespace \ - --set "image.registry=$(CONTAINER_REPO)" \ - --namespace $(DRACON_NS) \ - --set "image.tag=$(DRACON_VERSION)" \ - --wait + --install \ + --values ./deploy/deduplication-db-migrations/values/dev.yaml \ + --create-namespace \ + --set "image.registry=$(CONTAINER_REPO)" \ + --namespace $(DRACON_NS) \ + --set "image.tag=$(DRACON_VERSION)" \ + --wait @echo "Installing Components" # we are setting the container repo to it's own value so that we can override it from other make targets @@ -267,23 +275,35 @@ install-oss-components: --values ./deploy/deduplication-db-migrations/values/dev.yaml @echo "Done! Bumped version to $(DRACON_VERSION)" -dev-build-oss-components: cmd/draconctl/bin +dev-build-oss-components: @echo "Building open-source components for local dracon instance..." - $(eval CONTAINER_REPO:=localhost:5000) + $(eval GOOS:=linux) + $(eval GOARCH:=amd64) + $(eval CONTAINER_REPO:=localhost:5000/ocurity/dracon) + $(eval TMP_DIR:=tmp) + @mkdir $(TMP_DIR) + $(MAKE) cmd/draconctl/bin $(MAKE) -j 16 publish-component-containers CONTAINER_REPO=$(CONTAINER_REPO) - @./bin/cmd/draconctl components package \ - --version $(DRACON_VERSION) \ - --chart-version $(DRACON_VERSION) \ - --name $(DRACON_OSS_COMPONENTS_NAME) \ - ./components + @docker run \ + --platform $(GOOS)/$(GOARCH) \ + -v ./components:/components \ + -v ./tmp:/tmp \ + $(CONTAINER_REPO)/draconctl:$(DRACON_VERSION) components package \ + --version $(DRACON_VERSION) \ + --chart-version $(DRACON_VERSION) \ + --name $(DRACON_OSS_COMPONENTS_NAME) \ + ./components + @rm -r $(TMP_DIR) dev-dracon: - $(eval CONTAINER_REPO:=localhost:5000) + $(eval GOOS:=linux) + $(eval GOARCH:=amd64) + $(eval CONTAINER_REPO:=localhost:5000/ocurity/dracon) $(eval DRACON_OSS_COMPONENTS_PACKAGE_URL:=./$(DRACON_OSS_COMPONENTS_NAME)-$(DRACON_VERSION).tgz) - $(eval IN_CLUSTER_CONTAINER_REPO:=kind-registry:5000) - - $(MAKE) -j 16 publish-containers CONTAINER_REPO=$(CONTAINER_REPO) + $(eval IN_CLUSTER_CONTAINER_REPO:=kind-registry:5000/ocurity/dracon) + + $(MAKE) -j 16 draconctl-image-publish CONTAINER_REPO=$(CONTAINER_REPO) $(MAKE) -j 16 dev-build-oss-components CONTAINER_REPO=$(CONTAINER_REPO) $(MAKE) install CONTAINER_REPO=$(IN_CLUSTER_CONTAINER_REPO) DRACON_OSS_COMPONENTS_PACKAGE_URL=$(DRACON_OSS_COMPONENTS_PACKAGE_URL) diff --git a/components/consumers/dependency-track/Dockerfile b/components/consumers/dependency-track/Dockerfile deleted file mode 100644 index 17a6cd3ae..000000000 --- a/components/consumers/dependency-track/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM alpine:3.20 - -RUN apk add --no-cache ca-certificates - -COPY ./components/consumers/dependency-track/dependency-track /app/components/consumers/dependency-track/dependency-track - -ENTRYPOINT ["/app/components/consumers/dependency-track/dependency-track"] diff --git a/components/consumers/dependency-track/Makefile b/components/consumers/dependency-track/Makefile deleted file mode 100644 index b160f0073..000000000 --- a/components/consumers/dependency-track/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: container publish - -CONTAINER_REPO= -DRACON_VERSION= -SOURCE_CODE_REPO= -PRODUCER_AGGREGATOR_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "scratch") - -DOCKER=docker - -container: - $(DOCKER) build --tag $(CONTAINER_REPO)/components/consumers/dependency-track:$(DRACON_VERSION) \ - --file Dockerfile \ - $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - ../../../bin 1>&2 - -publish: - $(DOCKER) push $(CONTAINER_REPO)/components/consumers/dependency-track:$(DRACON_VERSION) 1>&2 diff --git a/components/consumers/dependency-track/main.go b/components/consumers/dependency-track/main.go index bd02d89f4..cfea8da05 100644 --- a/components/consumers/dependency-track/main.go +++ b/components/consumers/dependency-track/main.go @@ -28,7 +28,8 @@ var ( client *dtrack.Client ownerAnnotation string // used for debugging, turns off certificate and enables debug - debugDT bool + debugDTConnection string + debugDT bool ) func main() { @@ -37,7 +38,7 @@ func main() { flag.StringVar(&projectName, "projectName", "", "dependency track project name") flag.StringVar(&projectUUID, "projectUUID", "", "dependency track project name") flag.StringVar(&projectVersion, "projectVersion", "", "dependency track project version") - flag.BoolVar(&debugDT, "debugDependencyTrackConnection", false, "setup client with no tls and enable debug") + flag.StringVar(&debugDTConnection, "debugDependencyTrackConnection", "false", "setup client with no tls and enable debug") flag.StringVar( &ownerAnnotation, "ownerAnnotation", @@ -65,7 +66,10 @@ func main() { if projectVersion == "" { log.Fatal("project version is mandatory for dependency track") } - + if debugDTConnection == "true" { + debugDT = true + slog.Info("running in debug mode, skipping certificate verification and printing requests and responses") + } c, err := dtrack.NewClient( authURL, dtrack.WithHttpClient( diff --git a/components/consumers/elasticsearch/main.go b/components/consumers/elasticsearch/main.go index 58ea61038..81c3d3c5a 100644 --- a/components/consumers/elasticsearch/main.go +++ b/components/consumers/elasticsearch/main.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "log" + "log/slog" "strings" "time" @@ -14,33 +15,28 @@ import ( "github.com/ocurity/dracon/pkg/enumtransformers" "github.com/ocurity/dracon/pkg/templating" - // TODO: Support multiple versions of ES elasticsearch "github.com/elastic/go-elasticsearch/v8" ) var ( - esUrls string - esAddrs []string - esIndex string + esUrls string + esAddrs []string + esIndex string + + esAPIKey string + esCloudID string + basicAuthUser string basicAuthPass string issueTemplate string ) -func init() { - flag.StringVar(&esUrls, "es-urls", "", "[OPTIONAL] URLs to connect to elasticsearch comma separated. Can also use env variable ELASTICSEARCH_URL") - flag.StringVar(&esIndex, "es-index", "", "the index in elasticsearch to push results to") - flag.StringVar(&basicAuthUser, "basic-auth-user", "", "[OPTIONAL] the basic auth username") - flag.StringVar(&basicAuthPass, "basic-auth-pass", "", "[OPTIONAL] the basic auth password") - flag.StringVar(&issueTemplate, "descriptionTemplate", "", "a Go Template string describing how to show Raw or Enriched issues") -} - func parseFlags() error { if err := consumers.ParseFlags(); err != nil { return err } - if len(esIndex) < 1 { - return fmt.Errorf("es-index is undefined") + if len(esIndex) == 0 { + return fmt.Errorf("esIndex '%s' is undefined", esIndex) } if len(esUrls) > 0 { for _, u := range strings.Split(esUrls, ",") { @@ -51,6 +47,19 @@ func parseFlags() error { } func main() { + flag.StringVar(&esIndex, "esIndex", "", "the index in elasticsearch to push results to") + flag.StringVar(&issueTemplate, "descriptionTemplate", "", "a Go Template string describing how to show Raw or Enriched issues") + + // es SaaS options + flag.StringVar(&esAPIKey, "esAPIKey", "", "the api key in elasticsearch to contact results to") + flag.StringVar(&esCloudID, "esCloudID", "", "the cloud id in elasticsearch to contact results to") + + // es self-hosted options + flag.StringVar(&esUrls, "esURL", "", "[OPTIONAL] URLs to connect to elasticsearch comma separated. Can also use env variable ELASTICSEARCH_URL") + flag.StringVar(&basicAuthUser, "basic-auth-user", "", "[OPTIONAL] the basic auth username") + flag.StringVar(&basicAuthPass, "basic-auth-pass", "", "[OPTIONAL] the basic auth password") + flag.Parse() + if err := parseFlags(); err != nil { log.Fatal(err) } @@ -61,15 +70,16 @@ func main() { } if consumers.Raw { - log.Print("Parsing Raw results") + slog.Debug("Parsing Raw results") responses, err := consumers.LoadToolResponse() if err != nil { log.Fatal("could not load raw results, file malformed: ", err) } + numIssues := 0 for _, res := range responses { scanStartTime := res.GetScanInfo().GetScanStartTime().AsTime() + numIssues += len(res.GetIssues()) for _, iss := range res.GetIssues() { - log.Printf("Pushing %d, issues to es \n", len(responses)) b, err := getRawIssue(scanStartTime, res, iss) if err != nil { log.Fatal("Could not parse raw issue", err) @@ -81,26 +91,29 @@ func main() { } } } + slog.Info("Pushed", "numIssues", numIssues, "issues to Elasticsearch", "") } else { log.Print("Parsing Enriched results") responses, err := consumers.LoadEnrichedToolResponse() if err != nil { log.Fatal("could not load enriched results, file malformed: ", err) } + numIssues := 0 for _, res := range responses { scanStartTime := res.GetOriginalResults().GetScanInfo().GetScanStartTime().AsTime() + numIssues += len(res.GetIssues()) for _, iss := range res.GetIssues() { b, err := getEnrichedIssue(scanStartTime, res, iss) if err != nil { log.Fatal("Could not parse enriched issue", err) } res, err := es.Index(esIndex, bytes.NewBuffer(b)) - log.Printf("%+v", res) - if err != nil { - log.Fatal("Could not push enriched issue", err) + if err != nil || res.IsError() { + log.Fatal("Could not push enriched issue", err, "received", res.StatusCode) } } } + slog.Info("Pushed", "numIssues", numIssues, "issues to Elasticsearch", "") } } @@ -194,7 +207,12 @@ func getESClient() (*elasticsearch.Client, error) { esConfig.Username = basicAuthUser esConfig.Password = basicAuthPass } - + if esAPIKey != "" { + esConfig.APIKey = esAPIKey + } + if esCloudID != "" { + esConfig.CloudID = esCloudID + } if len(esAddrs) > 0 { esConfig.Addresses = esAddrs } @@ -217,11 +235,8 @@ func getESClient() (*elasticsearch.Client, error) { if err := json.NewDecoder(res.Body).Decode(&info); err != nil { return nil, err } - switch info.Version.Number[0] { - case '8': - // noop - we support this version - default: - err = fmt.Errorf("unsupported ES Server version %s", info.Version.Number) + if info.Version.Number[0] != '8' { + return nil, fmt.Errorf("unsupported ES Server version %s", info.Version.Number) } return es, err } diff --git a/components/consumers/elasticsearch/main_test.go b/components/consumers/elasticsearch/main_test.go index f1032682d..718a5293a 100644 --- a/components/consumers/elasticsearch/main_test.go +++ b/components/consumers/elasticsearch/main_test.go @@ -11,7 +11,6 @@ import ( v1 "github.com/ocurity/dracon/api/proto/v1" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -55,21 +54,21 @@ func TestEsPushBasicAuth(t *testing.T) { if r.Method == http.MethodGet { uname, pass, ok := r.BasicAuth() - assert.Equal(t, uname, "foo") - assert.Equal(t, pass, "bar") - assert.Equal(t, ok, true) + require.Equal(t, uname, "foo") + require.Equal(t, pass, "bar") + require.Equal(t, ok, true) _, err = w.Write([]byte(info)) require.NoError(t, err) } else if r.Method == http.MethodPost { // assert non authed operation (write results to index) - assert.Equal(t, buf.String(), string(esIn)) - assert.Equal(t, r.RequestURI, "/"+esIndex+"/_doc") + require.Equal(t, buf.String(), string(esIn)) + require.Equal(t, r.RequestURI, "/"+esIndex+"/_doc") uname, pass, ok := r.BasicAuth() - assert.Equal(t, uname, "foo") - assert.Equal(t, pass, "bar") - assert.Equal(t, ok, true) + require.Equal(t, uname, "foo") + require.Equal(t, pass, "bar") + require.Equal(t, ok, true) _, err = w.Write([]byte(want)) require.NoError(t, err) @@ -99,8 +98,8 @@ func TestEsPush(t *testing.T) { _, err = w.Write([]byte(info)) } else if r.Method == http.MethodPost { // assert non authed operation (write results to index) - assert.Equal(t, buf.String(), string(esIn)) - assert.Equal(t, r.RequestURI, "/"+esIndex+"/_doc") + require.Equal(t, buf.String(), string(esIn)) + require.Equal(t, r.RequestURI, "/"+esIndex+"/_doc") _, err = w.Write([]byte(want)) } require.NoError(t, err) @@ -112,3 +111,39 @@ func TestEsPush(t *testing.T) { _, err = client.Index(esIndex, bytes.NewBuffer(esIn)) require.NoError(t, err) } +func TestEsPushAPIKey(t *testing.T) { + esIndex = "dracon-es-test" + + esStub := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(r.Body) + require.NoError(t, err) + + w.Header().Set("X-Elastic-Product", "Elasticsearch") + w.WriteHeader(http.StatusOK) + + require.Equal(t, r.Header.Get("Authorization"), "APIKey foo") + + if r.Method == http.MethodGet { + _, err = w.Write([]byte(info)) + require.NoError(t, err) + } else if r.Method == http.MethodPost { + // assert non authed operation (write results to index) + require.Equal(t, buf.String(), string(esIn)) + require.Equal(t, r.RequestURI, "/"+esIndex+"/_doc") + + _, err = w.Write([]byte(want)) + require.NoError(t, err) + } + })) + defer esStub.Close() + os.Setenv("ELASTICSEARCH_URL", esStub.URL) + + // apikey ops + esAPIKey = "foo" + esCloudID = esStub.Config.Addr + client, err := getESClient() + require.NoError(t, err) + _, err = client.Index(esIndex, bytes.NewBuffer(esIn)) + require.NoError(t, err) +} diff --git a/components/consumers/elasticsearch/task.yaml b/components/consumers/elasticsearch/task.yaml index 714af7af3..ad33e59fe 100644 --- a/components/consumers/elasticsearch/task.yaml +++ b/components/consumers/elasticsearch/task.yaml @@ -14,6 +14,18 @@ spec: - name: consumer-elasticsearch-description-template type: string default: "" + - name: consumer-elasticsearch-api-key + type: string + default: "" + - name: consumer-elasticsearch-index-name + type: string + default: "" + - name: consumer-elasticsearch-index + type: string + default: "" + - name: consumer-elasticsearch-cloud-id + type: string + default: "" workspaces: - name: output description: The workspace containing the source-code to scan. @@ -22,11 +34,11 @@ spec: imagePullPolicy: IfNotPresent image: '{{ default "ghcr.io/ocurity/dracon" .Values.image.registry }}/components/consumers/elasticsearch:{{ .Chart.AppVersion }}' command: ["/app/components/consumers/elasticsearch/elasticsearch"] - env: - - name: ELASTICSEARCH_URL - value: "$(params.consumer-elasticsearch-url)" args: [ "-in", "$(workspaces.output.path)/.dracon/enrichers/", - "-es-index", "dracon", - "-descriptionTemplate","$(params.consumer-elasticsearch-description-template)" + "-descriptionTemplate","$(params.consumer-elasticsearch-description-template)", + "-esIndex", "$(params.consumer-elasticsearch-index-name)", + "-esAPIKey", "$(params.consumer-elasticsearch-api-key)", + "-esURL", "$(params.consumer-elasticsearch-url)", + "-esCloudID", "$(params.consumer-elasticsearch-cloud-id)", ] diff --git a/components/consumers/jira/Dockerfile b/components/consumers/jira/Dockerfile deleted file mode 100644 index 003b59928..000000000 --- a/components/consumers/jira/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM alpine:3.20 - -RUN apk add --no-cache ca-certificates - -COPY ./components/consumers/jira/jira /app/components/consumers/jira/jira - -ENTRYPOINT ["/app/components/consumers/jira/jira"] diff --git a/components/consumers/jira/Makefile b/components/consumers/jira/Makefile deleted file mode 100644 index bb2408a88..000000000 --- a/components/consumers/jira/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: container publish - -CONTAINER_REPO= -DRACON_VERSION= -SOURCE_CODE_REPO= -PRODUCER_AGGREGATOR_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "scratch") - -DOCKER=docker - -container: - $(DOCKER) build --tag $(CONTAINER_REPO)/components/consumers/jira:$(DRACON_VERSION) \ - --file Dockerfile \ - $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - ../../../bin 1>&2 - -publish: - $(DOCKER) push $(CONTAINER_REPO)/components/consumers/jira:$(DRACON_VERSION) 1>&2 diff --git a/components/consumers/pdf/Dockerfile b/components/consumers/pdf/Dockerfile index 3946bbbef..b2ffb4a6b 100644 --- a/components/consumers/pdf/Dockerfile +++ b/components/consumers/pdf/Dockerfile @@ -1,11 +1,11 @@ FROM golang:latest WORKDIR /playwright -RUN go mod init github.com/ocurity/pdf-consumer &&\ - go get -u github.com/playwright-community/playwright-go &&\ - go run github.com/playwright-community/playwright-go/cmd/playwright@latest install --with-deps +RUN go mod init github.com/ocurity/pdf-consumer && \ + go get -u github.com/playwright-community/playwright-go && \ + go run github.com/playwright-community/playwright-go/cmd/playwright@latest install --with-deps ENV PATH="${PATH}:/go/pkg/mod/github.com/playwright-community" COPY components/consumers/pdf/pdf /playwright/pdf COPY components/consumers/pdf/default.html /playwright/default.html -ENTRYPOINT ["/playwright/pdf"] \ No newline at end of file +ENTRYPOINT ["/playwright/pdf"] diff --git a/components/consumers/slack/Dockerfile b/components/consumers/slack/Dockerfile deleted file mode 100644 index 8d0a1774e..000000000 --- a/components/consumers/slack/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM alpine:3.20 - -RUN apk add --no-cache ca-certificates - -COPY ./components/consumers/slack/slack /app/components/consumers/slack/slack - -ENTRYPOINT ["/app/components/consumers/slack/slack"] \ No newline at end of file diff --git a/components/consumers/slack/Makefile b/components/consumers/slack/Makefile deleted file mode 100644 index abf3c1326..000000000 --- a/components/consumers/slack/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: container publish - -CONTAINER_REPO= -DRACON_VERSION= -SOURCE_CODE_REPO= -PRODUCER_AGGREGATOR_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "scratch") - -DOCKER=docker - -container: - $(DOCKER) build --tag $(CONTAINER_REPO)/components/consumers/slack:$(DRACON_VERSION) \ - --file Dockerfile \ - $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - ../../../bin 1>&2 - -publish: - $(DOCKER) push $(CONTAINER_REPO)/components/consumers/slack:$(DRACON_VERSION) 1>&2 diff --git a/components/producers/aggregator/Dockerfile b/components/producers/aggregator/Dockerfile deleted file mode 100644 index 3f74d3b26..000000000 --- a/components/producers/aggregator/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -ARG PRODUCER_AGGREGATOR_BASE_IMAGE -FROM ${PRODUCER_AGGREGATOR_BASE_IMAGE} - -COPY ./components/producers/aggregator/aggregator-parser /app/components/producers/aggregator/tagger - -ENTRYPOINT ["/app/components/producers/aggregator/tagger"] diff --git a/components/producers/aggregator/Makefile b/components/producers/aggregator/Makefile index bc174363f..2e3bcc0c8 100644 --- a/components/producers/aggregator/Makefile +++ b/components/producers/aggregator/Makefile @@ -3,15 +3,17 @@ CONTAINER_REPO= DRACON_VERSION= SOURCE_CODE_REPO= -PRODUCER_AGGREGATOR_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "scratch") +BUILD_ARCHITECTURE= DOCKER=docker container: $(DOCKER) build --tag $(CONTAINER_REPO)/components/producers/tagger:$(DRACON_VERSION) \ - --file Dockerfile \ + --build-arg EXECUTABLE_SRC_PATH=components/producers/aggregator/$(BUILD_ARCHITECTURE)/aggregator-parser \ + --build-arg EXECUTABLE_TARGET_PATH=components/producers/aggregator/tagger \ $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - --build-arg PRODUCER_AGGREGATOR_BASE_IMAGE=$(PRODUCER_AGGREGATOR_BASE_IMAGE) ../../../bin 1>&2 + --platform "$(BUILD_ARCHITECTURE)" \ + --file "${BASE_IMAGE_PATH}" ../../../bin 1>&2 publish: $(DOCKER) push $(CONTAINER_REPO)/components/producers/tagger:$(DRACON_VERSION) 1>&2 diff --git a/components/producers/github-code-scanning/Dockerfile b/components/producers/github-code-scanning/Dockerfile deleted file mode 100644 index 8fe764be4..000000000 --- a/components/producers/github-code-scanning/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM alpine:3.20 - -RUN apk add --no-cache ca-certificates - -COPY ./components/producers/github-code-scanning/github-code-scanning-parser /app/components/producers/github-code-scanning/github-code-scanning-parser - -ENTRYPOINT ["/app/components/producers/github-code-scanning/github-code-scanning-parser"] diff --git a/components/producers/github-code-scanning/Makefile b/components/producers/github-code-scanning/Makefile deleted file mode 100644 index 949c455e1..000000000 --- a/components/producers/github-code-scanning/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: container publish - -CONTAINER_REPO= -DRACON_VERSION= -SOURCE_CODE_REPO= -PRODUCER_AGGREGATOR_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "scratch") - -DOCKER=docker - -container: - $(DOCKER) build --tag $(CONTAINER_REPO)/components/producers/github-code-scanning:$(DRACON_VERSION) \ - --file Dockerfile \ - $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - ../../../bin 1>&2 - -publish: - $(DOCKER) push $(CONTAINER_REPO)/components/producers/github-code-scanning:$(DRACON_VERSION) 1>&2 diff --git a/components/producers/ossf-scorecard/Dockerfile b/components/producers/ossf-scorecard/Dockerfile deleted file mode 100644 index d81c6bc86..000000000 --- a/components/producers/ossf-scorecard/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -ARG OSSF_SCORECARD_SAFETY_BASE_IMAGE -FROM gcr.io/openssf/scorecard:stable - -FROM ${OSSF_SCORECARD_SAFETY_BASE_IMAGE} - -COPY --from=0 /scorecard /scorecard - -ENTRYPOINT ["/scorecard"] \ No newline at end of file diff --git a/components/producers/ossf-scorecard/Makefile b/components/producers/ossf-scorecard/Makefile deleted file mode 100644 index e93faa1f5..000000000 --- a/components/producers/ossf-scorecard/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -.PHONY: extras publish-extras - -CONTAINER_REPO= -DRACON_VERSION= -OSSF_SCORECARD_SAFETY_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "alpine:latest") - -DOCKER=docker - -extras: - $(DOCKER) build --tag $(CONTAINER_REPO)/components/producers/ossf-scorecard/scorecard-dracon:$(DRACON_VERSION) \ - --file Dockerfile \ - $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - --build-arg OSSF_SCORECARD_SAFETY_BASE_IMAGE=$(OSSF_SCORECARD_SAFETY_BASE_IMAGE) ../../.. 1>&2 - -publish-extras: - $(DOCKER) push $(CONTAINER_REPO)/components/producers/ossf-scorecard/scorecard-dracon:$(DRACON_VERSION) 1>&2 diff --git a/components/producers/typescript-eslint/eslint-wrapper/Dockerfile b/components/producers/typescript-eslint/eslint-wrapper/Dockerfile index 134be31fd..a5847bfd9 100644 --- a/components/producers/typescript-eslint/eslint-wrapper/Dockerfile +++ b/components/producers/typescript-eslint/eslint-wrapper/Dockerfile @@ -1,10 +1,11 @@ ARG ESLINT_WRAPPER_BASE_IMAGE -FROM ${ESLINT_WRAPPER_BASE_IMAGE} +FROM ${ESLINT_WRAPPER_BASE_IMAGE:-node:lts} +ARG BUILD_ARCHITECTURE WORKDIR /home/node/workspace COPY components/producers/typescript-eslint/eslint-wrapper/eslintrc.js /home/node/workspace COPY components/producers/typescript-eslint/eslint-wrapper/package.json /home/node/workspace -COPY bin/components/producers/typescript-eslint/eslint-wrapper/eslint-wrapper-parser /home/node/workspace/ +COPY bin/components/producers/typescript-eslint/eslint-wrapper/${BUILD_ARCHITECTURE}/eslint-wrapper-parser /home/node/workspace/ RUN npm uninstall --save bcrypt &&\ npm install --save-dev \ @@ -14,4 +15,4 @@ RUN npm uninstall --save bcrypt &&\ eslint-plugin-no-unsanitized \ eslint-plugin-security-node -ENTRYPOINT [ "/home/node/workspace/eslint-wrapper-parser"] +ENTRYPOINT ["/home/node/workspace/eslint-wrapper-parser"] diff --git a/components/producers/typescript-eslint/eslint-wrapper/Makefile b/components/producers/typescript-eslint/eslint-wrapper/Makefile index a4e344bfc..845557f1b 100644 --- a/components/producers/typescript-eslint/eslint-wrapper/Makefile +++ b/components/producers/typescript-eslint/eslint-wrapper/Makefile @@ -2,7 +2,7 @@ CONTAINER_REPO= DRACON_VERSION= -ESLINT_WRAPPER_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "node:lts") +BUILD_ARCHITECTURE= DOCKER=docker @@ -10,4 +10,6 @@ container: $(DOCKER) build --tag $(CONTAINER_REPO)/components/producers/typescript-eslint/eslint-wrapper:$(DRACON_VERSION) \ --file Dockerfile \ $$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - --build-arg ESLINT_WRAPPER_BASE_IMAGE=$(ESLINT_WRAPPER_BASE_IMAGE) ../../../.. 1>&2 + --build-arg ESLINT_WRAPPER_BASE_IMAGE=$(ESLINT_WRAPPER_BASE_IMAGE) \ + --build-arg BUILD_ARCHITECTURE=$(BUILD_ARCHITECTURE) \ + ../../../.. 1>&2 diff --git a/containers/Dockerfile.base b/containers/Dockerfile.base new file mode 100644 index 000000000..5f3c57bae --- /dev/null +++ b/containers/Dockerfile.base @@ -0,0 +1,21 @@ +FROM golang:alpine AS builder + +ARG EXECUTABLE_SRC_PATH +ARG EXECUTABLE_TARGET_PATH +ENV EXECUTABLE_TARGET_PATH=${EXECUTABLE_TARGET_PATH} +COPY ${EXECUTABLE_SRC_PATH} /app/${EXECUTABLE_TARGET_PATH} + +RUN apk update && \ + apk upgrade && \ + apk add --no-cache ca-certificates && \ + update-ca-certificates && \ + ln -s /app/${EXECUTABLE_TARGET_PATH} /bin/component + +FROM scratch + +ARG EXECUTABLE_TARGET_PATH +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /app/${EXECUTABLE_TARGET_PATH} /app/${EXECUTABLE_TARGET_PATH} +COPY --from=builder /bin/component /bin/component + +ENTRYPOINT ["/bin/component"] diff --git a/containers/Dockerfile.draconctl b/containers/Dockerfile.draconctl index 01b66bdfe..05ef77304 100644 --- a/containers/Dockerfile.draconctl +++ b/containers/Dockerfile.draconctl @@ -1,9 +1,13 @@ ARG BASE_MIGRATION_IMAGE + FROM ${BASE_MIGRATION_IMAGE:-scratch} -LABEL org.opencontainers.image.description "Draconctl is a command line tool for interacting with Dracon, you can find documentation for it at github.com/ocurity/dracon" +ARG GOOS=linux +ARG GOARCH=amd64 + +LABEL org.opencontainers.image.description="Draconctl is a command line tool for interacting with Dracon, you can find documentation for it at github.com/ocurity/dracon" -COPY ./bin/cmd/draconctl /bin/draconctl +COPY ./bin/cmd/${GOOS}/${GOARCH}/draconctl /bin/draconctl COPY ./pkg/enrichment/migrations /etc/dracon/migrations/enrichment -ENTRYPOINT [ "/bin/draconctl" ] +ENTRYPOINT ["/bin/draconctl"] diff --git a/deploy/dracon/chart/values.yaml b/deploy/dracon/chart/values.yaml index 333da2551..95a75adf2 100644 --- a/deploy/dracon/chart/values.yaml +++ b/deploy/dracon/chart/values.yaml @@ -38,9 +38,10 @@ arangodb: className: "" host: "" -postgresql: - # if set, a PostgreSQL instance will be deployed - enabled: true +# this section controls the database that components have access to +# the database should use the Postgres dialect. +# the database is used by the deduplication enricher +database: &psqlConfig host: "" auth: username: "" @@ -50,19 +51,20 @@ postgresql: querystringargs: "" fullnameOverride: "" +postgresql: + # if set, a PostgreSQL instance will be deployed + enabled: true + <<: *psqlConfig + + # this section controls aspects of managing a database used to store deduplication enrichments # the database should use the Postgres dialect. deduplication-db-migrations: # if set, a Job will be deployed that applies migrations to the deduplication database # the Job will run as part of the post-install/post-upgrade hook enabled: true - database: - host: "" - auth: - username: "" - password: "" - database: "" - querystringargs: "" + database: *psqlConfig + image: # registry to use for all diff --git a/deploy/dracon/values/dev.yaml b/deploy/dracon/values/dev.yaml index 1ea59896e..c6b807faa 100644 --- a/deploy/dracon/values/dev.yaml +++ b/deploy/dracon/values/dev.yaml @@ -30,28 +30,22 @@ arangodb: image: registry: kind-registry:5000/ocurity/dracon -postgresql: - enabled: true - -database: +database: &psqlConfig + host: dracon-postgresql:5432 auth: username: dracon password: dracon database: dracon postgresPassword: dracon querystringargs: "sslmode=disable" - host: dracon-postgresql:5432 + +postgresql: + enabled: true + <<: *psqlConfig tekton: enabled: true deduplication-db-migrations: enabled: true - database: - auth: - username: dracon - password: dracon - database: dracon - postgresPassword: dracon - querystringargs: "sslmode=disable" - host: dracon-postgresql:5432 + database: *psqlConfig diff --git a/docs/code-style/go/README.md b/docs/code-style/go/README.md new file mode 100644 index 000000000..0b115b375 --- /dev/null +++ b/docs/code-style/go/README.md @@ -0,0 +1,4 @@ +# Code Style + +* [Enumeration Generation](enumeration-generation.md) +* [Style](style.md) diff --git a/docs/code-style/enumeration-generation.md b/docs/code-style/go/enumeration-generation.md similarity index 100% rename from docs/code-style/enumeration-generation.md rename to docs/code-style/go/enumeration-generation.md diff --git a/docs/code-style/go/style.md b/docs/code-style/go/style.md new file mode 100644 index 000000000..352e2b8d3 --- /dev/null +++ b/docs/code-style/go/style.md @@ -0,0 +1,208 @@ +# Smithy Go Style Guide + +* [Introduction](#introduction) +* [Guidelines](#guidelines) + * [Pointers to Interfaces](#pointers-to-interfaces) + * [Don't Panic](#dont-panic) + +## Introduction + +Styles are the conventions that govern our code. The term style is a bit of a +misnomer, since these conventions cover far more than just source file +formatting—gofmt handles that for us. + +The goal of this guide is to manage this complexity by describing in detail the +Dos and Don'ts of writing Go code at Uber. These rules exist to keep the code +base manageable while still allowing engineers to use Go language features +productively. + +This documents idiomatic conventions in Go code that we follow at Smithy. A lot +of these are general guidelines for Go, while others extend upon external +resources: + +1. [Effective Go](https://go.dev/doc/effective_go) +2. [Go Common Mistakes](https://go.dev/wiki/CommonMistakes) +3. [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments) +4. [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md) + +## Guidelines + +### Pointers to Interfaces + +You almost never need a pointer to an interface. You should be passing +interfaces as values—the underlying data can still be a pointer. + +An interface is two fields: + +1. A pointer to some type-specific information. You can think of this as + "type." +2. Data pointer. If the data stored is a pointer, it’s stored directly. If + the data stored is a value, then a pointer to the value is stored. + +If you want interface methods to modify the underlying data, you must use a +pointer. + +Pointer to interfaces are quite tedious to dereference. + + + + + +
BadGood
+ +```go +type ( + Shape interface { + Area() float64 + } + + Circle struct { + Radius float64 + } +) + +func (c *Circle) Area() float64 { + return 3.14 * c.Radius * c.Radius +} + +func printArea(s *Shape) { + fmt.Println((*s).Area()) // Dereferencing pointer to interface +} + +func main() { + c := &Circle{Radius: 5} + + var s Shape = c // Assign Circle to Shape + printArea(&s) // Passing pointer to interface (bad) +} +``` + + + +```go +type ( + Shape interface { + Area() float64 + } + + Circle struct { + Radius float64 + } +) + +func (c *Circle) Area() float64 { + return 3.14 * c.Radius * c.Radius +} + +func printArea(s Shape) { + fmt.Println(s.Area()) // No need to dereference +} + +func main() { + c := &Circle{Radius: 5} + + var s Shape = c // Assign Circle to Shape + printArea(s) // Passing interface by value +} +``` + +
+ +### Don't Panic + +Code running in production must avoid panics. Panics are a major source of +[cascading failures](https://en.wikipedia.org/wiki/Cascading_failure). +If an error occurs, the function must return an error and +allow the caller to decide how to handle it. + + + + + +
BadGood
+ +```go +func run(args []string) { + if len(args) == 0 { + panic("an argument is required") + } + // ... +} + +func main() { + run(os.Args[1:]) +} +``` + + + +```go +func run(args []string) error { + if len(args) == 0 { + return errors.New("an argument is required") + } + // ... + return nil +} + +func main() { + if err := run(os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} +``` + +
+ +Panic/recover is not an error handling strategy. +A program must panic only when +something irrecoverable happens such as a nil dereference. +An exception to this is +program initialization: bad things at program startup that +should abort the program may cause panic. + +```go +var _statusTemplate = template.Must(template.New("name").Parse("_statusHTML")) +``` + +Even in tests, prefer `t.Fatal` or `t.FailNow` over panics to ensure that the +test is marked as failed. + + + + + +
BadGood
+ +```go +// func TestFoo(t *testing.T) + +f, err := os.CreateTemp("", "test") +if err != nil { + panic("failed to set up test") +} +``` + + + +```go +// func TestFoo(t *testing.T) + +f, err := os.CreateTemp("", "test") +if err != nil { + t.Fatal("failed to set up test") +} +``` + +
+ +Panics should always be reported in a way that +the team is aware that a service is having such +issue. A tool like [Sentry](https://sentry.io/welcome/) +is excellent to report such extreme issues and make +sure that the team is notified to resolve the root cause. + +[It's useful to see the stacktrace at the moment +of a process panicking](https://yourbasic.org/golang/recover-from-panic/). +Logs, Traces and Metrics should be enriched with the latter. diff --git a/docs/getting-started.md b/docs/getting-started.md index ce6903a55..d4b4a57c7 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -176,7 +176,8 @@ KiND cluster, that's not the case. Instead, the registry's host is deploy the pipelines and their image repositories will also have to be set to this value.* -*\*\*Make sure that you use the draconctl image that you pushed in the repository.* +*\*\*Make sure that you use the draconctl image that you pushed in the +repository.* #### Using a different base image for your images @@ -191,6 +192,45 @@ these components have their own Makefiles. In those cases you can place a `.custom_image` file in the directory with the base image you wish to use and that will be picked up by the Makefile and build the container. +#### Building binaries and images for non linux/amd64 architecture + +*\*Useful for Apple Silicon chips users.* + +###### Containers + +If you need your images to be built for non linux/amd64 architecture, +you can supply the flag `CONTAINER_OS_ARCH` for customisation of containers. + +This can be passed to the make commands used to build images, for example: + +```bash +make CONTAINER_OS_ARCH=linux/arm64 components +``` + +or: + +```bash +make CONTAINER_OS_ARCH=linux/arm64 publish-containers +``` + +By default, when `CONTAINER_ARCH` is not supplied, `linux/amd64` is used. + +###### Binaries + +`GOOS` and `GOARCH` can be supplied for customisation of the go binaries. + +These can be passed to the make commands used to build binaries, for example: + +```bash +make GOOS=linux GOARCH=arm64 component-binaries +``` + +By default `linux` and `amd64` are used. + +\**For Apple Silicon chips, you might want to use +`GOOS=darwin` and `GOARCH=arm64` when building binaries +locally for development.* + #### Deploying your custom Dracon components Helm package You can package your components into a Helm package by running the following diff --git a/package-lock.json b/package-lock.json index a7f7bfbf0..98cec1b1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,9 +4,8 @@ "requires": true, "packages": { "": { - "name": "dracon", "devDependencies": { - "remark-cli": "^12.0.0", + "remark-cli": "^12.0.1", "remark-lint-list-item-indent": "^4.0.0", "remark-lint-no-shell-dollars": "^4.0.0", "remark-preset-lint-consistent": "^6.0.0", @@ -698,10 +697,11 @@ } }, "node_modules/import-meta-resolve": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", - "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -915,16 +915,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/load-plugin/node_modules/import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -1838,12 +1828,13 @@ } }, "node_modules/remark-cli": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-12.0.0.tgz", - "integrity": "sha512-IGxCo2VsXC/GS2YdlF7+S8DsUiyULyiauik01NFoiMIrOlbDhXjrKLD8hYazwQdD67nw2k7cwOBIxcK/cbNd9Q==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-12.0.1.tgz", + "integrity": "sha512-2NAEOACoTgo+e+YAaCTODqbrWyhMVmlUyjxNCkTrDRHHQvH6+NbrnqVvQaLH/Q8Ket3v90A43dgAJmXv8y5Tkw==", "dev": true, + "license": "MIT", "dependencies": { - "import-meta-resolve": "^3.0.0", + "import-meta-resolve": "^4.0.0", "markdown-extensions": "^2.0.0", "remark": "^15.0.0", "unified-args": "^11.0.0" diff --git a/package.json b/package.json index 386220237..1def70236 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ ] }, "devDependencies": { - "remark-cli": "^12.0.0", + "remark-cli": "^12.0.1", "remark-lint-list-item-indent": "^4.0.0", "remark-lint-no-shell-dollars": "^4.0.0", "remark-preset-lint-consistent": "^6.0.0", diff --git a/scripts/build_component_binary.sh b/scripts/build_component_binary.sh index 64d97e651..1dc3380ac 100755 --- a/scripts/build_component_binary.sh +++ b/scripts/build_component_binary.sh @@ -19,6 +19,11 @@ echo "${1}" | grep -Eq ^components/consumers/.*$ && executable="${executable}" | executable_src_path=$(dirname "${1}") executable_path=$(dirname $(dirname "${1}"))/"${executable}" -echo "building bin/${executable_path}/${executable}" > /dev/stderr +# Customised binary per OS/ARCH. +GOOS=${GOOS:-$(go env GOOS)} +GOARCH=${GOARCH:-$(go env GOARCH)} +out_bin_path="bin/${executable_src_path}/${GOOS}/${GOARCH}/${executable}" -go build -o "bin/${executable_src_path}/${executable}" "./${executable_src_path}/main.go" +echo "building $out_bin_path" > /dev/stderr + +CGO_ENABLED=0 go build -o $out_bin_path "./${executable_src_path}/main.go" diff --git a/scripts/build_component_container.sh b/scripts/build_component_container.sh index 2ad5e8d9e..860ff4485 100755 --- a/scripts/build_component_container.sh +++ b/scripts/build_component_container.sh @@ -4,38 +4,45 @@ set -e; source ./scripts/util.sh +# Sanity check for not arguments being passed. if [ "$#" -eq 0 ] then - util::error "No directory provided to build" + util::error "No directory argument provided to build." exit 1 fi -executable=$(basename $(dirname "${1}")) +dir_name="$1" +GOOS="${GOOS}" +GOARCH="${GOARCH}" +build_architecture="${GOOS}/${GOARCH}" -echo "${1}" | grep -Eq ^components/producers/.*$ && executable="${executable}-parser" || true -echo "${1}" | grep -Eq ^components/enrichers/.*$ && executable="${executable}" || true -echo "${1}" | grep -Eq ^components/consumers/.*$ && executable="${executable}" || true +executable=$(basename $(dirname ${dir_name})) -executable_src_path=$(dirname "${1}") -executable_path=$(dirname "${1}")/"${executable}" +echo ${dir_name} | grep -Eq ^components/producers/.*$ && executable="${executable}-parser" || true +echo ${dir_name} | grep -Eq ^components/enrichers/.*$ && executable="${executable}" || true +echo ${dir_name} | grep -Eq ^components/consumers/.*$ && executable="${executable}" || true -if make -C "${executable_src_path}" --no-print-directory --dry-run container >/dev/null 2>&1 +EXECUTABLE_SRC_PATH="$(dirname ${dir_name})/${build_architecture}/${executable}" +COMPONENT_PATH="$(dirname ${dir_name})" +EXECUTABLE_TARGET_PATH="${COMPONENT_PATH}/${executable}" + +BASE_IMAGE_PATH=$(realpath ${BASE_IMAGE_PATH:-./containers/Dockerfile.base}) + +if make -C "${COMPONENT_PATH}" --no-print-directory --dry-run container >/dev/null 2>&1 then - make -C "${executable_src_path}" --no-print-directory --quiet container CONTAINER_REPO="${CONTAINER_REPO}" DRACON_VERSION="${DRACON_VERSION}" + make -C "${COMPONENT_PATH}" --no-print-directory --quiet container BASE_IMAGE_PATH="${BASE_IMAGE_PATH}" CONTAINER_REPO="${CONTAINER_REPO}" DRACON_VERSION="${DRACON_VERSION}" BUILD_ARCHITECTURE="${build_architecture}" else - dockerfile_template=" - FROM ${BASE_IMAGE:-scratch} \n - COPY ${executable_path} /app/${executable_path} \n - ENTRYPOINT ["/app/${executable_path}"] \n - " - dockerfile_path=$(mktemp) - printf "${dockerfile_template}" > "${dockerfile_path}" - docker build -t "${CONTAINER_REPO}/${executable_src_path}:${DRACON_VERSION}" \ + docker build \ + --build-arg EXECUTABLE_SRC_PATH="${EXECUTABLE_SRC_PATH}" \ + --build-arg EXECUTABLE_TARGET_PATH="${EXECUTABLE_TARGET_PATH}" \ + --tag "${CONTAINER_REPO}/${COMPONENT_PATH}:${DRACON_VERSION}" \ $([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \ - -f "${dockerfile_path}" ./bin + --file "${BASE_IMAGE_PATH}" \ + --platform "${build_architecture}" \ + ./bin fi -if make -C "${executable_src_path}" --no-print-directory --dry-run extras >/dev/null 2>&1 +if make -C "${COMPONENT_PATH}" --no-print-directory --dry-run extras >/dev/null 2>&1 then - make -C "${executable_src_path}" --no-print-directory --quiet extras CONTAINER_REPO="${CONTAINER_REPO}" DRACON_VERSION="${DRACON_VERSION}" + make -C "${COMPONENT_PATH}" --no-print-directory --quiet extras CONTAINER_REPO="${CONTAINER_REPO}" DRACON_VERSION="${DRACON_VERSION}" fi