Skip to content

Commit

Permalink
re-implement git-initializer component using git2go (#1939)
Browse files Browse the repository at this point in the history
Signed-off-by: Kent Rancourt <kent.rancourt@microsoft.com>
  • Loading branch information
krancour authored May 4, 2022
1 parent 6ff548c commit 340421d
Show file tree
Hide file tree
Showing 13 changed files with 455 additions and 446 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ test-unit-go:
cd sdk/v3 && \
go test \
-v \
-tags testUnit \
-timeout=60s \
-race \
-coverprofile=coverage.txt \
Expand All @@ -111,6 +112,7 @@ test-unit-go:
cd ../../v2 && \
go test \
-v \
-tags testUnit \
-timeout=60s \
-race \
-coverprofile=coverage.txt \
Expand Down
3 changes: 2 additions & 1 deletion golangci.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
run:
concurrency: 1
deadline: 10m
skip-files:
build-tags:
- lint

linters:
disable-all: true
Expand Down
71 changes: 53 additions & 18 deletions v2/git-initializer-windows/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,59 @@
FROM golang:1.17.2-windowsservercore-1809 as builder
# escape=`

ARG VERSION
ARG COMMIT
ENV CGO_ENABLED=0
FROM brigadecore/win-go-tools:v0.2.0 as builder

WORKDIR /src
COPY sdk/ sdk/
WORKDIR /src/v2
COPY v2/git-initializer/ git-initializer/
COPY v2/internal/ internal/
COPY v2/go.mod go.mod
COPY v2/go.sum go.sum
# As of this writing, pacman installs a newer libgit2 than we are able to use.
# Here, we download and install an older version.
RUN curl `
-L `
https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-libgit2-1.2.0-3-any.pkg.tar.zst `
-o C:\windows\temp/libgit2.pkg.tar.zst `
&& bash -l -c "pacman --noconfirm -U /c/windows/temp/libgit2.pkg.tar.zst"

RUN go build \
-o ../bin/git-initializer.exe \
-ldflags \"-w -X github.com/brigadecore/brigade-foundations/version.version=$env:VERSION -X github.com/brigadecore/brigade-foundations/version.commit=$env:COMMIT\" \
./git-initializer
WORKDIR C:\\msys64\\brigade
COPY sdk\\ sdk\\
WORKDIR C:\\msys64\\brigade\\v2
COPY v2\\go.mod go.mod
COPY v2\\go.sum go.sum
RUN bash -l -c " `
cd /brigade/v2 `
&& PATH=$PATH:/mingw64/bin `
GOROOT=/mingw64/lib/go `
go mod download `
"
COPY v2\\git-initializer\\ git-initializer\\
COPY v2\\internal\\ internal\\

FROM mcr.microsoft.com/windows/nanoserver:1809
# The `-tags static,system_libgit2` specified below instruct git2go on how to
# locate libgit2. It does NOT imply our binary is statically linked -- in fact,
# it is not. In theory, that can be accomplished by including
# `-extldflags 'static'` in the `-ldflags`, but we've had no luck getting that
# to work.
RUN bash -l -c " `
cd /brigade/v2 `
&& PATH=$PATH:/mingw64/bin `
GOROOT=/mingw64/lib/go `
go build `
-tags static,system_libgit2 `
-ldflags \"-w -X github.com/brigadecore/brigade-foundations/version.version=$VERSION -X github.com/brigadecore/brigade-foundations/version.commit=$COMMIT\" `
-o ../bin/git-initializer.exe `
./git-initializer `
"

COPY --from=builder /src/bin/ /brigade/bin/
ENTRYPOINT [ "C:\\msys64\\brigade\\bin\\git-initializer.exe" ]

ENTRYPOINT ["/brigade/bin/git-initializer.exe"]
FROM mcr.microsoft.com/windows/nanoserver:1809 AS final

USER ContainerAdministrator

# Note that because we were unable to produce a statically-linked binary, we
# depend on a number of .dlls from the builder image, so we just copy them
# all over and add them to the path.
COPY --from=builder C:\\msys64\\mingw64\\bin\\ C:\\mingw64\\bin\\
RUN setx /M PATH "%PATH%;C:\mingw64\bin"

COPY --from=builder C:\\msys64\\brigade\\bin\\ C:\\brigade\\bin\\

USER ContainerUser

ENTRYPOINT ["C:\\brigade\\bin\\git-initializer.exe"]
14 changes: 7 additions & 7 deletions v2/git-initializer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
FROM --platform=$BUILDPLATFORM brigadecore/go-tools:v0.9.0 as builder
FROM brigadecore/go-libgit2:v0.1.0 as builder

ARG VERSION
ARG COMMIT
ARG TARGETOS
ARG TARGETARCH
ENV CGO_ENABLED=0

WORKDIR /src
COPY sdk/ sdk/
Expand All @@ -15,12 +12,15 @@ RUN go mod download
COPY v2/git-initializer/ git-initializer/
COPY v2/internal/ internal/

RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build \
# Despite CGO being involved, this builds a statically linked binary
RUN go build \
-tags static,system_libgit2 \
-o ../bin/git-initializer \
-ldflags "-w -X github.com/brigadecore/brigade-foundations/version.version=$VERSION -X github.com/brigadecore/brigade-foundations/version.commit=$COMMIT" \
-ldflags "-extldflags '-static -lgcrypt -lgpg-error' -w -X github.com/brigadecore/brigade-foundations/version.version=$VERSION -X github.com/brigadecore/brigade-foundations/version.commit=$COMMIT" \
./git-initializer

FROM gcr.io/distroless/static:nonroot as final
# Note: Cannot use gcr.io/distroless/static:nonroot because we still need glibc
FROM gcr.io/distroless/base:nonroot as final

COPY --from=builder /src/bin/ /brigade/bin/

Expand Down
82 changes: 82 additions & 0 deletions v2/git-initializer/credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//go:build !testUnit && !lint
// +build !testUnit,!lint

// We exclude this file from unit tests and linting because it cannot be
// compiled without CGO and a specific version of libgit2 pre-installed. To keep
// our linting and unit tests lightweight, those are complications we'd like to
// avoid. We'll live without the linting and test this well with integration
// tests.

package main

import (
git "github.com/libgit2/git2go/v32"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)

// getCredentialsCallback extracts credentials, if any, from project secrets in
// the provided map and returns a callback function. If no credentials are found
// in the secrets, a nil function pointer is returned.
func getCredentialsCallback(
secrets map[string]string,
) (git.CredentialsCallback, error) {
// We'll check the project's secrets first for a well-known key that we could
// expect contains a private SSH key. If we find that, we'll use it.
if privateKey, ok := secrets["gitSSHKey"]; ok {
var signer ssh.Signer
var err error
// The private key may or may not be protected by a passphrase...
if keyPassStr, ok := secrets["gitSSHKeyPassword"]; ok {
// Auth using key and passphrase
signer, err = ssh.ParsePrivateKeyWithPassphrase(
[]byte(privateKey),
[]byte(keyPassStr),
)
} else {
// Auth using a key without a passphrase
signer, err = ssh.ParsePrivateKey([]byte(privateKey))
}
if err != nil {
return nil, errors.Wrap(
err,
`error parsing private SSH key specified by secret "gitSSHKey"`,
)
}
return func(
string,
string,
git.CredentialType,
) (*git.Credential, error) {
return git.NewCredentialSSHKeyFromSigner("git", signer)
}, nil
}

// Check the project's secrets for a well-known key that we could expect
// contains a password or token.
if password, ok := secrets["gitPassword"]; ok {
// There may or may not be a username associated with the password. It
// really depends on who hosts the repository we're cloning from. GitHub,
// for instance, expects the username to be any non-empty string (and the
// password to be a personal access token), while Bitbucket expects a valid
// username (and the password to be an app password).
username := secrets["gitUsername"]
// Ultimately, the username and password are used with basic auth and an
// empty username isn't allowed, so if no username was specified (e.g. for a
// repo hosted on GitHub), just use the string "git" as the username.
if username == "" {
username = "git"
}
return func(
string,
string,
git.CredentialType,
) (*git.Credential, error) {
// Basic auth
return git.NewCredentialUserpassPlaintext(username, password)
}, nil
}

// No credentials found
return nil, nil
}
36 changes: 36 additions & 0 deletions v2/git-initializer/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"encoding/json"
"io/ioutil"

"github.com/brigadecore/brigade/sdk/v3"
"github.com/pkg/errors"
)

// event is a custom representation of a Brigade event that matches what the
// API server provides to the git-initializer.
type event struct {
Project struct {
Secrets map[string]string `json:"secrets"`
} `json:"project"`
Worker struct {
Git *sdk.GitConfig `json:"git"`
} `json:"worker"`
}

// getEvent loads an event from the indicated path on the file system.
func getEvent(path string) (event, error) {
evt := event{}
eventPath := "/var/event/event.json"
data, err := ioutil.ReadFile(eventPath)
if err != nil {
return evt,
errors.Wrapf(err, "error reading event from file %q", eventPath)
}
return evt, errors.Wrapf(
json.Unmarshal(data, &evt),
"error reading event from file %q",
eventPath,
)
}
Loading

0 comments on commit 340421d

Please sign in to comment.