Skip to content

Commit

Permalink
#2 working checkout build
Browse files Browse the repository at this point in the history
  • Loading branch information
BrobotDubstep committed Jun 1, 2020
1 parent 86f392e commit eaa8701
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 120 deletions.
2 changes: 1 addition & 1 deletion build/package/harbour-build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o harbour-build /module/cmd/harbour-build

# final stage
FROM scratch
COPY --from=build-env /module/cmd/harbour-build .
COPY --from=build-env /module/harbour-build .
ENTRYPOINT ["/harbour-build"]
1 change: 1 addition & 0 deletions build/package/harbour-build/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web
5 changes: 4 additions & 1 deletion pkg/apiclient/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ func Post(ctx context.Context, url string, response interface{}, body interface{
return
}

err = handleResponse(ctx, resp, response)
if response != nil {
err = handleResponse(ctx, resp, response)
}

return
}

Expand Down
73 changes: 36 additions & 37 deletions pkg/harbourbuild/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ type Builder struct {
cli *client.Client
ctx context.Context
ctxPath string
repoPath string
redisOptions redisconfig.RedisOptions
log *logrus.Entry
}

func NewBuilder(jobChan chan models.BuildJob, ctxPath string, repoPath string, redisConfig redisconfig.RedisOptions) (Builder, error) {
func NewBuilder(jobChan chan models.BuildJob, ctxPath string, redisConfig redisconfig.RedisOptions) (Builder, error) {
var builder Builder
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return builder, err
}

builder = Builder{jobChan: jobChan, cli: cli, ctx: ctx, ctxPath: ctxPath, repoPath: repoPath, redisOptions: redisConfig}
builder = Builder{jobChan: jobChan, cli: cli, ctx: ctx, ctxPath: ctxPath, redisOptions: redisConfig}
return builder, nil
}

Expand All @@ -53,102 +53,107 @@ func (b Builder) Start() {
}

func (b Builder) buildImage(job models.BuildJob) {
log := logrus.WithField("reqId", job.ReqId)
b.log = logrus.WithField("reqId", job.ReqId)
redisClient := redisconfig.OpenClient(b.redisOptions)

if err := redisClient.HSet(job.BuildKey, "build_status", "Running").Err(); err != nil {
log.WithError(err).Error("Failed to save data to redis")
b.log.WithError(err).Error("Failed to save data to redis")
return
}

buildCtx, err := b.createBuildContext(job.Request.SCMId)
buildCtx, err := b.createBuildContext(job.FilePath, job.Dockerfile)
if err != nil {
log.WithError(err).Error("Failed to create build context")
b.log.WithError(err).Error("Failed to create build context")
return
}

tag := []string{b.getImageString(job.RegistryUrl, job.Request.SCMId, job.Request.Tag)}
tag := []string{b.getImageString(job.RegistryUrl, job.Repository, job.Tag)}
opt := types.ImageBuildOptions{
Tags: tag,
Dockerfile: job.Request.Dockerfile,
Dockerfile: job.Dockerfile,
}

resp, err := b.cli.ImageBuild(b.ctx, buildCtx, opt)
if err != nil {
log.WithError(err).Error("Failed to build image")
b.log.WithError(err).Error("Failed to build image")
return
}

defer func() {
err := buildCtx.Close()
//err = os.Remove(buildCtx.Name())
err = os.Remove(buildCtx.Name())
err = os.RemoveAll(job.FilePath)
err = resp.Body.Close()
if err != nil {
log.WithError(err).Error("Error while cleaning up build context")
b.log.WithError(err).Error("Error while cleaning up build context")
return
}
}()

buf := new(bytes.Buffer)
_, err = buf.ReadFrom(resp.Body)
if err != nil {
log.WithError(err).Error("Failed to parse response body")
b.log.WithError(err).Error("Failed to parse response body")
}

logs := buf.String()
if strings.Contains(logs, "errorDetail") {
b.log.Errorf("Build failed: %s", logs)
if err := redisClient.HSet(job.BuildKey, "build_status", "Failed", "logs", logs).Err(); err != nil {
log.WithError(err).Error("Failed to save data to redis")
b.log.WithError(err).Error("Failed to save data to redis")
return
}
return
}

if err := redisClient.HSet(job.BuildKey, "build_status", "Success", "logs", logs).Err(); err != nil {
log.WithError(err).Error("Failed to save data to redis")
b.log.WithError(err).Error("Failed to save data to redis")
return
}
log.Tracef("Image %s was built", job.Request.SCMId)
b.log.Tracef("Image %s was built", job.Repository)

imageString := b.getImageString(job.RegistryUrl, job.Request.SCMId, job.Request.Tag)
imageString := b.getImageString(job.RegistryUrl, job.Repository, job.Tag)

if err = b.pushImage(imageString, job.RegistryToken); err != nil {
log.WithError(err).Error("Error while pushing image to registry")
b.log.WithError(err).Error("Error while pushing image to registry")
return
}

log.Tracef("Image %s was pushed to registry %s", job.Request.SCMId, job.RegistryUrl)
b.log.Tracef("Image %s was pushed to registry %s", job.Repository, job.RegistryUrl)
}

func (b Builder) createBuildContext(project string) (*os.File, error) {
func (b Builder) createBuildContext(filePath string, dockerfile string) (*os.File, error) {
var excludes = []string{}
buildContext := fmt.Sprintf(b.ctxPath+"%s.tar", project)
projectPath, err := b.getProjectPath(project)
if err != nil {
return nil, err
}
buildContext := fmt.Sprintf("%s/%s.tar", b.ctxPath, strings.Split(filePath, "/")[1])

fmt.Println(projectPath)

ignore, err := os.Open(fmt.Sprintf("%s/%s", projectPath, ".dockerignore"))
path := fmt.Sprintf("./%s%s%s", filePath, dockerfile, ".dockerignore")
ignore, err := os.Open(path)
if os.IsNotExist(err) {
b.log.Trace("Fallback to root .dockerignore")
ignore, err = os.Open(fmt.Sprintf("%s/%s", filePath, ".dockerignore"))
if os.IsNotExist(err) {
b.log.Trace("No .dockerignore found")
}
}

excludes, err = dockerignore.ReadAll(ignore)
if err != nil {

}

fmt.Println(excludes)
patternMatcher, err := fileutils.NewPatternMatcher(excludes)
if err != nil {

}

tar := new(archivex.TarFile)
err = tar.Create(buildContext)
err = filepath.Walk(projectPath, func(path string, info os.FileInfo, err error) error {
split := strings.Split(filepath.Clean(path), project+"/")
if err != nil {
logrus.WithError(err).Error("Couldn't create build context")
}

err = filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error {
split := strings.Split(filepath.Clean(path), filePath+"/")

if len(split) > 1 {
excluded, _ := patternMatcher.Matches(split[1])
Expand Down Expand Up @@ -214,9 +219,3 @@ func (b Builder) getImageString(registryUrl string, repository string, tag strin
}
return fmt.Sprintf("%s/%s", strings.Split(registryUrl, "//")[1], repository)
}

//TODO Communicate with Harbour SCM in order to receive the path to the project-files
func (b Builder) getProjectPath(project string) (string, error) {
// Just returns a demo path
return fmt.Sprintf(b.repoPath+"%s", project), nil
}
3 changes: 0 additions & 3 deletions pkg/harbourbuild/configuration/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

type Options struct {
ContextPath string
RepoPath string
Redis redisconfig.RedisOptions
OIDCConfig auth.OIDCConfig
DockerRegistry registry.RegistryConfig
Expand All @@ -18,7 +17,6 @@ type Options struct {
func NewDefaultOptions() *Options {
s := Options{
ContextPath: "",
RepoPath: "",
Redis: redisconfig.NewDefaultRedisOptions(),
OIDCConfig: auth.DefaultConfig(),
}
Expand All @@ -30,7 +28,6 @@ func ParseViperConfig() *Options {
s := NewDefaultOptions()

s.ContextPath = viper.GetString("CONTEXT_PATH")
s.RepoPath = viper.GetString("REPO_PATH")
s.OIDCConfig = auth.ParseViperConfig()
s.Redis = redisconfig.ParseViperConfig()
s.DockerRegistry = registry.ParseViperConfig()
Expand Down
81 changes: 32 additions & 49 deletions pkg/harbourbuild/handler/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,69 @@ package handler
import (
"context"
"errors"
"fmt"
"github.com/google/uuid"
"github.com/go-redis/redis/v7"
"github.com/harbourrocks/harbour/pkg/apiclient"
"github.com/harbourrocks/harbour/pkg/auth"
"github.com/harbourrocks/harbour/pkg/harbourbuild/configuration"
"github.com/harbourrocks/harbour/pkg/harbourbuild/models"
"github.com/harbourrocks/harbour/pkg/harbourbuild/redis"
"github.com/harbourrocks/harbour/pkg/harbourgateway/model"
"github.com/harbourrocks/harbour/pkg/harbourscm/common"
"github.com/harbourrocks/harbour/pkg/harbourscm/worker"
"github.com/harbourrocks/harbour/pkg/httpcontext"
"github.com/harbourrocks/harbour/pkg/httphelper"
"github.com/harbourrocks/harbour/pkg/logconfig"
"github.com/harbourrocks/harbour/pkg/redisconfig"
registryModels "github.com/harbourrocks/harbour/pkg/registry/models"
l "github.com/sirupsen/logrus"
"net/http"
)

type BuilderModel struct {
type BuildHandler struct {
buildChan chan models.BuildJob
config *configuration.Options
}

func NewBuilderModel(buildChan chan models.BuildJob, config *configuration.Options) BuilderModel {
return BuilderModel{buildChan: buildChan, config: config}
func NewBuildHandler(buildChan chan models.BuildJob, config *configuration.Options) BuildHandler {
return BuildHandler{buildChan: buildChan, config: config}
}

func (b BuilderModel) Build(w http.ResponseWriter, r *http.Request) {
fmt.Println("Test")

w.WriteHeader(http.StatusOK)
}

// BuildImage
func (b BuilderModel) BuildImage(w http.ResponseWriter, r *http.Request) {
func (b BuildHandler) Build(w http.ResponseWriter, r *http.Request) {
log := logconfig.GetLogReq(r)
client := redisconfig.GetRedisClientReq(r)

buildKey := httphelper.GetQueryParam(r, "state")

var buildRequest models.BuildRequest
if err := httphelper.ReadRequest(r, w, &buildRequest); err != nil {
var checkoutResponse worker.CheckoutCompletedModel
if err := httphelper.ReadRequest(r, w, &checkoutResponse); err != nil {
log.WithError(err).Error("Failed to parse build request")
w.WriteHeader(http.StatusInternalServerError)
return
}

registryToken, err := fetchRegistryToken(r.Context(), buildRequest.SCMId, b.config)
if err != nil {
return // Error is already logged in get
if checkoutResponse.Success != true {
}

buildKey, err := createBuildEntry(r.Context(), buildRequest)
if err != nil {
log.WithError(err).Error("Failed to save build to redis")
redisBuildEntry := client.HGetAll(buildKey)
if err := redisBuildEntry.Err(); err != redis.Nil && err != nil {
l.WithError(err).Error("Failed to load build data")
w.WriteHeader(http.StatusInternalServerError)
return
}
buildEntry := redisBuildEntry.Val()

ctx := context.WithValue(r.Context(), "oidcTokenStr", buildEntry["token"])

repository := common.Decode(buildEntry["scm_id"])

registryToken, err := fetchRegistryToken(ctx, repository, b.config)
if err != nil {
return // Error is already logged in get
}

b.buildChan <- models.BuildJob{
Request: buildRequest,
Repository: repository,
Tag: buildEntry["tag"],
FilePath: checkoutResponse.WorkspacePath,
Dockerfile: buildEntry["dockerfile"],
BuildKey: buildKey,
RegistryToken: registryToken,
RegistryUrl: b.config.DockerRegistry.RegistryUrl,
Expand All @@ -66,31 +74,6 @@ func (b BuilderModel) BuildImage(w http.ResponseWriter, r *http.Request) {

log.Trace("Build job enqueued")
w.WriteHeader(http.StatusOK)

_ = httphelper.WriteResponse(r, w, model.Build{
BuildId: buildKey,
Status: "Pending",
})
}

func createBuildEntry(ctx context.Context, request models.BuildRequest) (string, error) {
client := redisconfig.GetRedisClientCtx(ctx)

buildId := uuid.New()
buildKey := redis.BuildKey(buildId.String())

err := client.HSet(buildKey,
"build_id", buildId.String(),
"repository", request.SCMId,
"commit", request.Commit,
"logs", nil,
"build_status", "Pending").Err()

if err != nil {
return buildKey, err
}

return buildKey, nil
}

func fetchRegistryToken(ctx context.Context, repository string, registry *configuration.Options) (string, error) {
Expand Down
Loading

0 comments on commit eaa8701

Please sign in to comment.