Skip to content
Closed
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
2 changes: 2 additions & 0 deletions docs/_includes/reference/vault_plugin/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Configure the plugin.

### Parameters

* `buildx_driver` (string, optional) — The buildx driver to build release artifacts with: docker-container (used by default) or kubernetes. Takes precedence over the TRDL_BUILDX_DRIVER environment variable.
* `buildx_driver_opts` (array, optional) — The buildx driver options, one --driver-opt per element (e.g. namespace=trdl-build), passed through as is. Take precedence over the TRDL_BUILDX_DRIVER_OPTS_* environment variables.
* `git_repo_url` (string, required) — URL of the Git repository.
* `git_trdl_channels_branch` (string, optional) — A special Git branch to store the trdl channels configuration file.
* `git_trdl_channels_path` (string, optional) — A path in the Git repository to the trdl channels configuration file (trdl_channels.yaml is used by default).
Expand Down
14 changes: 14 additions & 0 deletions docs/pages_en/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ TRDL_BUILDX_DRIVER_OPTS_SEPARATOR=;
TRDL_BUILDX_DRIVER_OPTS_KUBE='namespace=trdl-build;rootless=true'
```

The same two settings are also available per project in the plugin configuration, which is the only way to reach them when the plugin is compiled into a host process whose environment the administrator cannot set:

* `buildx_driver` — the same values as `TRDL_BUILDX_DRIVER`;
* `buildx_driver_opts` — a list of `--driver-opt` values, one per element, each passed through as is.

```json
{
"buildx_driver": "kubernetes",
"buildx_driver_opts": ["namespace=trdl-build", "rootless=true"]
}
```

Each of the two settings is resolved on its own: the plugin configuration takes precedence over the environment, and the environment takes precedence over the default `docker-container` driver with no options. A field left out of `configure`, or set to an empty value, means "not configured" and falls back to the environment — it does not override it with an empty value. To build with no driver options at all while the environment defines some, unset those variables.

Notes on the `kubernetes` driver:

* the target namespace must exist, and the Vault process needs permissions to manage Deployments and Pods in it: the builder runs as a BuildKit Deployment and is removed after the build;
Expand Down
14 changes: 14 additions & 0 deletions docs/pages_ru/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ TRDL_BUILDX_DRIVER_OPTS_SEPARATOR=;
TRDL_BUILDX_DRIVER_OPTS_KUBE='namespace=trdl-build;rootless=true'
```

Те же две настройки доступны и в конфигурации плагина, отдельно для каждого проекта. Это единственный способ задать их, когда плагин вкомпилен в хост-процесс, окружением которого администратор не управляет:

* `buildx_driver` — те же значения, что и у `TRDL_BUILDX_DRIVER`;
* `buildx_driver_opts` — список значений `--driver-opt`, по одному в элементе, каждое передаётся без изменений.

```json
{
"buildx_driver": "kubernetes",
"buildx_driver_opts": ["namespace=trdl-build", "rootless=true"]
}
```

Каждая из двух настроек разрешается независимо: конфигурация плагина имеет приоритет над переменными окружения, а переменные окружения — над умолчанием, то есть драйвером `docker-container` без опций. Поле, не переданное в `configure` или переданное пустым, означает «не задано» и отдаёт решение переменным окружения, а не перекрывает их пустым значением. Чтобы собирать вообще без опций драйвера, когда в окружении они заданы, эти переменные нужно снять.

Особенности драйвера `kubernetes`:

* целевой namespace должен существовать, а процессу Vault нужны права на управление Deployment и Pod в нём: сборщик работает как BuildKit Deployment и удаляется после сборки;
Expand Down
43 changes: 32 additions & 11 deletions server/path_configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"

"github.com/werf/trdl/server/pkg/docker"
"github.com/werf/trdl/server/pkg/elf_signing"
"github.com/werf/trdl/server/pkg/git"
"github.com/werf/trdl/server/pkg/mac_signing"
Expand All @@ -30,6 +31,8 @@ const (
fieldNameS3AccessKeyID = "s3_access_key_id"
fieldNameS3SecretAccessKey = "s3_secret_access_key"
fieldNameS3BucketName = "s3_bucket_name"
fieldNameBuildxDriver = "buildx_driver"
fieldNameBuildxDriverOpts = "buildx_driver_opts"

storageKeyConfiguration = "configuration"
)
Expand Down Expand Up @@ -110,6 +113,16 @@ func configurePath(b *Backend) *framework.Path {
Description: "The S3 storage secret access key",
Required: true,
},
fieldNameBuildxDriver: {
Type: framework.TypeString,
Description: "The buildx driver to build release artifacts with: docker-container (used by default) or kubernetes. Takes precedence over the TRDL_BUILDX_DRIVER environment variable",
Required: false,
},
fieldNameBuildxDriverOpts: {
Type: framework.TypeStringSlice,
Description: "The buildx driver options, one --driver-opt per element (e.g. namespace=trdl-build), passed through as is. Take precedence over the TRDL_BUILDX_DRIVER_OPTS_* environment variables",
Required: false,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.CreateOperation: &framework.PathOperation{
Expand Down Expand Up @@ -137,6 +150,10 @@ func (b *Backend) pathConfigureCreateOrUpdate(ctx context.Context, req *logical.
return errResp, nil
}

if err := docker.ValidateBuildxDriver(fields.Get(fieldNameBuildxDriver).(string)); err != nil {
return logical.ErrorResponse("%s validation failed: %s", fieldNameBuildxDriver, err), nil
}

cfg := &configuration{
GitRepoUrl: fields.Get(fieldNameGitRepoUrl).(string),
GitTrdlPath: fields.Get(fieldNameGitTrdlPath).(string),
Expand All @@ -149,6 +166,8 @@ func (b *Backend) pathConfigureCreateOrUpdate(ctx context.Context, req *logical.
S3AccessKeyID: fields.Get(fieldNameS3AccessKeyID).(string),
S3SecretAccessKey: fields.Get(fieldNameS3SecretAccessKey).(string),
S3BucketName: fields.Get(fieldNameS3BucketName).(string),
BuildxDriver: fields.Get(fieldNameBuildxDriver).(string),
BuildxDriverOpts: fields.Get(fieldNameBuildxDriverOpts).([]string),
}

if err := putConfiguration(ctx, req.Storage, cfg); err != nil {
Expand Down Expand Up @@ -180,17 +199,19 @@ func (b *Backend) pathConfigureDelete(ctx context.Context, req *logical.Request,
}

type configuration struct {
GitRepoUrl string `structs:"git_repo_url" json:"git_repo_url"`
GitTrdlPath string `structs:"git_trdl_path" json:"git_trdl_path"`
GitTrdlChannelsPath string `structs:"git_trdl_channels_path" json:"git_trdl_channels_path"`
GitTrdlChannelsBranch string `structs:"git_trdl_channels_branch" json:"git_trdl_channels_branch"`
InitialLastPublishedGitCommit string `structs:"initial_last_published_git_commit" json:"initial_last_published_git_commit"`
RequiredNumberOfVerifiedSignaturesOnCommit int `structs:"required_number_of_verified_signatures_on_commit" json:"required_number_of_verified_signatures_on_commit"`
S3Endpoint string `structs:"s3_endpoint" json:"s3_endpoint"`
S3Region string `structs:"s3_region" json:"s3_region"`
S3AccessKeyID string `structs:"s3_access_key_id" json:"s3_access_key_id"`
S3SecretAccessKey string `structs:"s3_secret_access_key" json:"s3_secret_access_key"`
S3BucketName string `structs:"s3_bucket_name" json:"s3_bucket_name"`
GitRepoUrl string `structs:"git_repo_url" json:"git_repo_url"`
GitTrdlPath string `structs:"git_trdl_path" json:"git_trdl_path"`
GitTrdlChannelsPath string `structs:"git_trdl_channels_path" json:"git_trdl_channels_path"`
GitTrdlChannelsBranch string `structs:"git_trdl_channels_branch" json:"git_trdl_channels_branch"`
InitialLastPublishedGitCommit string `structs:"initial_last_published_git_commit" json:"initial_last_published_git_commit"`
RequiredNumberOfVerifiedSignaturesOnCommit int `structs:"required_number_of_verified_signatures_on_commit" json:"required_number_of_verified_signatures_on_commit"`
S3Endpoint string `structs:"s3_endpoint" json:"s3_endpoint"`
S3Region string `structs:"s3_region" json:"s3_region"`
S3AccessKeyID string `structs:"s3_access_key_id" json:"s3_access_key_id"`
S3SecretAccessKey string `structs:"s3_secret_access_key" json:"s3_secret_access_key"`
S3BucketName string `structs:"s3_bucket_name" json:"s3_bucket_name"`
BuildxDriver string `structs:"buildx_driver" json:"buildx_driver"`
BuildxDriverOpts []string `structs:"buildx_driver_opts" json:"buildx_driver_opts"`
}

func (cfg *configuration) RepositoryOptions() publisher.RepositoryOptions {
Expand Down
90 changes: 90 additions & 0 deletions server/path_configure_ai_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//go:build ai_tests

package server

import (
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/assert"
)

func (suite *PathConfigureCallbacksSuite) TestAI_CreateOrUpdate_BuildxFieldsOmitted() {
reqData := dataCompleteConfiguration()
delete(reqData, fieldNameBuildxDriver)
delete(reqData, fieldNameBuildxDriverOpts)

suite.req.Operation = logical.CreateOperation
suite.req.Data = reqData

resp, err := suite.backend.HandleRequest(suite.ctx, suite.req)
assert.Nil(suite.T(), err)
assert.Nil(suite.T(), resp)

cfg, err := getConfiguration(suite.ctx, suite.storage)
assert.Nil(suite.T(), err)
if assert.NotNil(suite.T(), cfg) {
assert.Empty(suite.T(), cfg.BuildxDriver)
assert.Empty(suite.T(), cfg.BuildxDriverOpts)
}
}

func (suite *PathConfigureCallbacksSuite) TestAI_CreateOrUpdate_UnsupportedBuildxDriver() {
reqData := dataCompleteConfiguration()
reqData[fieldNameBuildxDriver] = "docker"

suite.req.Operation = logical.CreateOperation
suite.req.Data = reqData

resp, err := suite.backend.HandleRequest(suite.ctx, suite.req)
assert.Nil(suite.T(), err)
if assert.NotNil(suite.T(), resp) {
assert.Contains(suite.T(), resp.Error().Error(), fieldNameBuildxDriver)
}

cfg, err := getConfiguration(suite.ctx, suite.storage)
assert.Nil(suite.T(), err)
assert.Nil(suite.T(), cfg)
}

func (suite *PathConfigureCallbacksSuite) TestAI_CreateOrUpdate_RejectedUpdateKeepsConfiguration() {
err := putConfiguration(suite.ctx, suite.storage, completeConfiguration())
assert.Nil(suite.T(), err)

reqData := dataCompleteConfiguration()
reqData[fieldNameBuildxDriver] = "docker"

suite.req.Operation = logical.UpdateOperation
suite.req.Data = reqData

resp, err := suite.backend.HandleRequest(suite.ctx, suite.req)
assert.Nil(suite.T(), err)
assert.NotNil(suite.T(), resp)

cfg, err := getConfiguration(suite.ctx, suite.storage)
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), completeConfiguration(), cfg)
}

// A configuration written before the buildx fields existed carries neither key.
func (suite *PathConfigureCallbacksSuite) TestAI_Read_ConfigurationStoredBeforeBuildxFields() {
entry := &logical.StorageEntry{
Key: storageKeyConfiguration,
Value: []byte(`{
"git_repo_url": "https://github.com/werf/trdl/server.git",
"required_number_of_verified_signatures_on_commit": 10,
"s3_endpoint": "trdl.s3.us-west-2.example.com",
"s3_region": "us-west-2",
"s3_access_key_id": "AKIAIOSFODNN7EXAMPLE",
"s3_secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"s3_bucket_name": "trdl"
}`),
}
assert.Nil(suite.T(), suite.storage.Put(suite.ctx, entry))

cfg, err := getConfiguration(suite.ctx, suite.storage)
assert.Nil(suite.T(), err)
if assert.NotNil(suite.T(), cfg) {
assert.Empty(suite.T(), cfg.BuildxDriver)
assert.Empty(suite.T(), cfg.BuildxDriverOpts)
assert.Equal(suite.T(), "trdl", cfg.S3BucketName)
}
}
4 changes: 4 additions & 0 deletions server/path_configure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ func dataCompleteConfiguration() map[string]interface{} {
fieldNameS3AccessKeyID: cfg.S3AccessKeyID,
fieldNameS3SecretAccessKey: cfg.S3SecretAccessKey,
fieldNameS3BucketName: cfg.S3BucketName,
fieldNameBuildxDriver: cfg.BuildxDriver,
fieldNameBuildxDriverOpts: cfg.BuildxDriverOpts,
}
}

Expand All @@ -138,5 +140,7 @@ func completeConfiguration() *configuration {
S3AccessKeyID: "AKIAIOSFODNN7EXAMPLE",
S3SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
S3BucketName: "trdl",
BuildxDriver: "kubernetes",
BuildxDriverOpts: []string{"namespace=trdl-build", "nodeselector=disktype=ssd,zone=a"},
}
}
12 changes: 7 additions & 5 deletions server/path_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,13 @@ func (b *Backend) pathRelease(ctx context.Context, req *logical.Request, fields
go func() {
err := docker.BuildReleaseArtifacts(ctx,
docker.BuildReleaseArtifactsOpts{
TarWriter: tarWriter,
GitRepo: gitRepo,
FromImage: trdlCfg.GetDockerImage(),
RunCommands: trdlCfg.Commands,
Storage: req.Storage,
TarWriter: tarWriter,
GitRepo: gitRepo,
FromImage: trdlCfg.GetDockerImage(),
RunCommands: trdlCfg.Commands,
Storage: req.Storage,
BuildxDriver: cfg.BuildxDriver,
BuildxDriverOpts: cfg.BuildxDriverOpts,
}, b.Logger())
if err != nil {
errCh <- err
Expand Down
14 changes: 9 additions & 5 deletions server/pkg/docker/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ const (
)

type BuildReleaseArtifactsOpts struct {
FromImage string
RunCommands []string
GitRepo *git.Repository
TarWriter *nio.PipeWriter
Storage logical.Storage
FromImage string
RunCommands []string
GitRepo *git.Repository
TarWriter *nio.PipeWriter
Storage logical.Storage
BuildxDriver string
BuildxDriverOpts []string
}

func BuildReleaseArtifacts(ctx context.Context, opts BuildReleaseArtifactsOpts, logger hclog.Logger) error {
Expand Down Expand Up @@ -100,6 +102,8 @@ func BuildReleaseArtifacts(ctx context.Context, opts BuildReleaseArtifactsOpts,
builder, err := NewBuilder(ctx, &NewBuilderOpts{
BuildId: buildId,
ContextPath: serviceDockerfilePathInContext,
BuildxDriver: opts.BuildxDriver,
BuildxDriverOpts: opts.BuildxDriverOpts,
Secrets: secrets,
MacSigningCredentials: credentials,
Logger: logger,
Expand Down
44 changes: 44 additions & 0 deletions server/pkg/docker/build_ai_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//go:build ai_tests

package docker

import (
"context"
"testing"

"github.com/djherbis/buffer"
"github.com/djherbis/nio/v3"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// The build stops at builder creation, before any docker invocation, so the
// error proves that the configured driver traveled from the release options
// all the way into the buildx arguments.
func TestAI_BuildReleaseArtifacts_ForwardsConfiguredDriver(t *testing.T) {
clearDriverOptsEnv(t)
t.Setenv(buildxDriverEnv, "kubernetes")

gitRepo, err := git.Init(memory.NewStorage(), memfs.New())
require.NoError(t, err)

_, tarWriter := nio.Pipe(buffer.New(1024))

err = BuildReleaseArtifacts(context.Background(), BuildReleaseArtifactsOpts{
FromImage: "alpine",
RunCommands: []string{"true"},
GitRepo: gitRepo,
TarWriter: tarWriter,
Storage: &logical.InmemStorage{},
BuildxDriver: "docker",
}, hclog.NewNullLogger())

require.Error(t, err)
assert.Contains(t, err.Error(), buildxDriverConfigurationSource)
assert.Contains(t, err.Error(), `"docker"`)
}
Loading