Skip to content

Commit

Permalink
feat: deployment config for expected params and targets (#1214)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecrupper authored Dec 18, 2024
1 parent aa973fe commit fa06a47
Show file tree
Hide file tree
Showing 23 changed files with 912 additions and 26 deletions.
24 changes: 17 additions & 7 deletions api/build/compile_publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import (

// CompileAndPublishConfig is a struct that contains information for the CompileAndPublish function.
type CompileAndPublishConfig struct {
Build *types.Build
Metadata *internal.Metadata
BaseErr string
Source string
Comment string
Labels []string
Retries int
Build *types.Build
Deployment *types.Deployment
Metadata *internal.Metadata
BaseErr string
Source string
Comment string
Labels []string
Retries int
}

// CompileAndPublish is a helper function to generate the queue items for a build. It takes a form
Expand Down Expand Up @@ -307,6 +308,15 @@ func CompileAndPublish(
errors.New(skip)
}

// validate deployment config
if (b.GetEvent() == constants.EventDeploy) && cfg.Deployment != nil {
if err := p.Deployment.Validate(cfg.Deployment.GetTarget(), cfg.Deployment.GetPayload()); err != nil {
retErr := fmt.Errorf("%s: failed to validate deployment for %s: %w", baseErr, repo.GetFullName(), err)

return nil, nil, http.StatusBadRequest, retErr
}
}

// check if the pipeline did not already exist in the database
if pipeline == nil {
pipeline = compiled
Expand Down
2 changes: 1 addition & 1 deletion api/deployment/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func GetDeployment(c *gin.Context) {
}

// send API call to database to capture the deployment
d, err := database.FromContext(c).GetDeployment(ctx, int64(number))
d, err := database.FromContext(c).GetDeploymentForRepo(ctx, r, int64(number))
if err != nil {
// send API call to SCM to capture the deployment
d, err = scm.FromContext(c).GetDeployment(ctx, u, r, int64(number))
Expand Down
128 changes: 128 additions & 0 deletions api/deployment/get_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: Apache-2.0

package deployment

import (
"errors"
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"gorm.io/gorm"

"github.com/go-vela/server/compiler"
"github.com/go-vela/server/database"
"github.com/go-vela/server/router/middleware/repo"
"github.com/go-vela/server/router/middleware/user"
"github.com/go-vela/server/scm"
"github.com/go-vela/server/util"
)

// swagger:operation GET /api/v1/deployments/{org}/{repo}/config deployments GetDeploymentConfig
//
// Get a deployment config
//
// ---
// produces:
// - application/json
// parameters:
// - in: path
// name: org
// description: Name of the organization
// required: true
// type: string
// - in: path
// name: repo
// description: Name of the repository
// required: true
// type: string
// - in: query
// name: ref
// description: Ref to target for the deployment config
// type: string
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully retrieved the deployment config
// schema:
// "$ref": "#/definitions/Deployment"
// '400':
// description: Invalid request payload or path
// schema:
// "$ref": "#/definitions/Error"
// '401':
// description: Unauthorized
// schema:
// "$ref": "#/definitions/Error"
// '404':
// description: Not found
// schema:
// "$ref": "#/definitions/Error"
// '500':
// description: Unexpected server error
// schema:
// "$ref": "#/definitions/Error"

// GetDeploymentConfig represents the API handler to get a deployment config at a given ref.
func GetDeploymentConfig(c *gin.Context) {
// capture middleware values
l := c.MustGet("logger").(*logrus.Entry)
r := repo.Retrieve(c)
u := user.Retrieve(c)

ctx := c.Request.Context()

// capture ref from parameters - use default branch if not provided
ref := util.QueryParameter(c, "ref", r.GetBranch())

entry := fmt.Sprintf("%s@%s", r.GetFullName(), ref)

l.Debugf("reading deployment config %s", entry)

var config []byte

// check if the pipeline exists in the database
p, err := database.FromContext(c).GetPipelineForRepo(ctx, ref, r)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
l.Debugf("pipeline %s not found in database, fetching from scm", entry)

config, err = scm.FromContext(c).ConfigBackoff(ctx, u, r, ref)
if err != nil {
retErr := fmt.Errorf("unable to get pipeline configuration for %s: %w", entry, err)

util.HandleError(c, http.StatusNotFound, retErr)

return
}
} else {
// some other error
retErr := fmt.Errorf("unable to get pipeline for %s: %w", entry, err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}
} else {
l.Debugf("pipeline %s found in database", entry)

config = p.GetData()
}

// set up compiler
compiler := compiler.FromContext(c).Duplicate().WithCommit(ref).WithRepo(r).WithUser(u)

// compile the pipeline
pipeline, _, err := compiler.CompileLite(ctx, config, nil, true)
if err != nil {
retErr := fmt.Errorf("unable to compile pipeline %s: %w", entry, err)

util.HandleError(c, http.StatusBadRequest, retErr)

return
}

c.JSON(http.StatusOK, pipeline.Deployment)
}
15 changes: 8 additions & 7 deletions api/webhook/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,13 +378,14 @@ func PostWebhook(c *gin.Context) {

// construct CompileAndPublishConfig
config := build.CompileAndPublishConfig{
Build: b,
Metadata: m,
BaseErr: baseErr,
Source: "webhook",
Comment: prComment,
Labels: prLabels,
Retries: 3,
Build: b,
Deployment: webhook.Deployment,
Metadata: m,
BaseErr: baseErr,
Source: "webhook",
Comment: prComment,
Labels: prLabels,
Retries: 3,
}

// generate the queue item
Expand Down
12 changes: 12 additions & 0 deletions compiler/native/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ func (c *client) CompileLite(ctx context.Context, v interface{}, ruleData *pipel
// create map of templates for easy lookup
templates := mapFromTemplates(p.Templates)

// expand deployment config
p, err = c.ExpandDeployment(ctx, p, templates)
if err != nil {
return nil, _pipeline, err
}

switch {
case len(p.Stages) > 0:
// inject the templates into the steps
Expand Down Expand Up @@ -330,6 +336,12 @@ func (c *client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api
return nil, _pipeline, err
}

// inject the template for deploy config if exists
p, err = c.ExpandDeployment(ctx, p, tmpls)
if err != nil {
return nil, _pipeline, err
}

// inject the templates into the steps
p, err = c.ExpandSteps(ctx, p, tmpls, r, c.GetTemplateDepth())
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion compiler/native/compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ func TestNative_Compile_StepsPipeline(t *testing.T) {
t.Errorf("Compile returned err: %v", err)
}

if diff := cmp.Diff(got, want); diff != "" {
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("Compile mismatch (-want +got):\n%s", diff)
}
}
Expand Down
51 changes: 51 additions & 0 deletions compiler/native/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,43 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin
return s, nil
}

// ExpandDeployment injects the template for a
// templated deployment config in a yaml configuration.
func (c *client) ExpandDeployment(ctx context.Context, b *yaml.Build, tmpls map[string]*yaml.Template) (*yaml.Build, error) {
if len(tmpls) == 0 {
return b, nil
}

if len(b.Deployment.Template.Name) == 0 {
return b, nil
}

// lookup step template name
tmpl, ok := tmpls[b.Deployment.Template.Name]
if !ok {
return b, fmt.Errorf("missing template source for template %s in pipeline for deployment config", b.Deployment.Template.Name)
}

bytes, err := c.getTemplate(ctx, tmpl, b.Deployment.Template.Name)
if err != nil {
return b, err
}

// initialize variable map if not parsed from config
if len(b.Deployment.Template.Variables) == 0 {
b.Deployment.Template.Variables = make(map[string]interface{})
}

tmplBuild, err := c.mergeDeployTemplate(bytes, tmpl, &b.Deployment)
if err != nil {
return b, err
}

b.Deployment = tmplBuild.Deployment

return b, nil
}

func (c *client) getTemplate(ctx context.Context, tmpl *yaml.Template, name string) ([]byte, error) {
var (
bytes []byte
Expand Down Expand Up @@ -368,6 +405,20 @@ func (c *client) mergeTemplate(bytes []byte, tmpl *yaml.Template, step *yaml.Ste
}
}

func (c *client) mergeDeployTemplate(bytes []byte, tmpl *yaml.Template, d *yaml.Deployment) (*yaml.Build, error) {
switch tmpl.Format {
case constants.PipelineTypeGo, "golang", "":
//nolint:lll // ignore long line length due to return
return native.Render(string(bytes), "", d.Template.Name, make(raw.StringSliceMap), d.Template.Variables)
case constants.PipelineTypeStarlark:
//nolint:lll // ignore long line length due to return
return starlark.Render(string(bytes), "", d.Template.Name, make(raw.StringSliceMap), d.Template.Variables, c.GetStarlarkExecLimit())
default:
//nolint:lll // ignore long line length due to return
return &yaml.Build{}, fmt.Errorf("format of %s is unsupported", tmpl.Format)
}
}

// helper function that creates a map of templates from a yaml configuration.
func mapFromTemplates(templates []*yaml.Template) map[string]*yaml.Template {
m := make(map[string]*yaml.Template)
Expand Down
Loading

0 comments on commit fa06a47

Please sign in to comment.