diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..0b18ed53d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +** +!bin/** +!dist/** +!.release/docker/** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24fdc6633..1d1e7bf32 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,10 +60,28 @@ jobs: project="$(go list -m)" sha="$(git rev-parse HEAD)" echo "::set-output name=ldflags::"-s -w -X \'$project/version.GitCommit=$sha\'"" + + get-product-edition: + runs-on: ubuntu-latest + outputs: + product-edition: ${{ steps.get-product-edition.outputs.product-edition}} + steps: + - uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: "1.18.1" + - name: Get product edition + id: get-product-edition + run: | + make edition + echo "::set-output name=product-edition::$(make edition)" + build-other: needs: - get-product-version + - get-product-edition - set-ld-flags runs-on: ubuntu-latest strategy: @@ -102,7 +120,7 @@ jobs: workflow: build-admin-ui.yaml commit: ${{ steps.set-sha.outputs.sha }} repo: "hashicorp/boundary-ui" - name: admin-ui + name: admin-ui-${{ needs.get-product-edition.outputs.product-edition }} path: internal/ui/.tmp/boundary-ui/ui/admin/dist - name: Go Build env: @@ -124,6 +142,7 @@ jobs: build-linux: needs: - get-product-version + - get-product-edition - set-ld-flags runs-on: ubuntu-latest strategy: @@ -157,7 +176,7 @@ jobs: workflow: build-admin-ui.yaml commit: ${{ steps.set-sha.outputs.sha }} repo: "hashicorp/boundary-ui" - name: admin-ui + name: admin-ui-${{ needs.get-product-edition.outputs.product-edition }} path: internal/ui/.tmp/boundary-ui/ui/admin/dist - name: Go Build env: @@ -209,6 +228,7 @@ jobs: build-darwin: needs: - get-product-version + - get-product-edition - set-ld-flags runs-on: macos-latest strategy: @@ -239,7 +259,7 @@ jobs: workflow: build-admin-ui.yaml commit: ${{ steps.set-sha.outputs.sha }} repo: "hashicorp/boundary-ui" - name: admin-ui + name: admin-ui-${{ needs.get-product-edition.outputs.product-edition }} path: internal/ui/.tmp/boundary-ui/ui/admin/dist - name: Go Build env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 48341934b..24a59ef26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. ### Bug Fixes +## 0.8.1 (2022/05/13) + +### Bug Fixes + * controller: Do not shut down cluster listener when it receives an invalid packet ([Issue](https://github.com/hashicorp/boundary/issues/2072), [PR](https://github.com/hashicorp/boundary/pull/2073)) diff --git a/Makefile b/Makefile index bdd427988..750ea7f46 100644 --- a/Makefile +++ b/Makefile @@ -327,3 +327,13 @@ ci-verify: # This is used for release builds by .github/workflows/build.yml version: @go run ./cmd/boundary version | awk '/Version Number:/ { print $$3 }' + +EDITION?= +.PHONY: edition +# This is used for release builds by .github/workflows/build.yml +edition: + @if [ -z "$(EDITION)" ]; then \ + go run ./cmd/boundary version -format=json | jq -r '.version_metadata // "oss"'; \ + else \ + echo $(EDITION); \ + fi; \ diff --git a/go.mod b/go.mod index efe694352..49f7063a5 100644 --- a/go.mod +++ b/go.mod @@ -90,7 +90,8 @@ require ( require github.com/hashicorp/go-dbw v0.0.0-20220412153211-c470aec9369f // this is a branch and should be updated before merging require ( - github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 + github.com/DATA-DOG/go-sqlmock v1.5.0 + github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b github.com/hashicorp/nodeenrollment v0.0.0-20220520141833-ad7323153acc ) @@ -98,7 +99,6 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/AlecAivazis/survey/v2 v2.2.9 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.1.1 // indirect @@ -172,7 +172,6 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/sethvargo/go-diceware v0.3.0 // indirect @@ -195,14 +194,5 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gorm.io/driver/sqlite v1.2.4 // indirect - modernc.org/cc/v3 v3.32.4 // indirect - modernc.org/ccgo/v3 v3.9.2 // indirect - modernc.org/libc v1.9.5 // indirect - modernc.org/mathutil v1.2.2 // indirect - modernc.org/memory v1.0.4 // indirect - modernc.org/opt v0.1.1 // indirect - modernc.org/sqlite v1.10.6 // indirect - modernc.org/strutil v1.1.0 // indirect - modernc.org/token v1.0.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 8698be076..7678a413e 100644 --- a/go.sum +++ b/go.sum @@ -377,7 +377,6 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNE github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= @@ -678,8 +677,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= -github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6 h1:3BknAN8P8326GPQac8e3iM132lwtwkO/3OilBU67S1I= -github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220512190651-85bdcdd684e6/go.mod h1:TzFKcCsMKyMwFiMfH2MfO8SXqsFQLxzR7Z8JZiGzpK4= +github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b h1:NmqGrxmUGR1XRhmYA3wsD2BktB9PfbCDIhXd4FM0hbk= +github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20220515130442-cac0b5ac133b/go.mod h1:puo5dXNWmXx4xoKCzbKSevcFQ4w+z72k3ZF3EVo6zI4= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2 h1:it114Kjpk79JVh6AfxFMP5oi1gb+RPzcSLiG3zF/J0w= github.com/hashicorp/go-kms-wrapping/plugin/v2 v2.0.2/go.mod h1:b9mq0bG5xJ10ZcTEQJLZ22O9y71hIt2MGTLqROA/SAM= github.com/hashicorp/go-kms-wrapping/v2 v2.0.6-0.20220512190651-85bdcdd684e6 h1:isD0/0pGWppbxkakRvnX4M4k65+ZSRW0yELW+twLPDk= @@ -1159,7 +1158,6 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1967,41 +1965,29 @@ k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAG k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/cc/v3 v3.32.4 h1:1ScT6MCQRWwvwVdERhGPsPq0f55J1/pFEOCiqM7zc78= modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= -modernc.org/ccgo/v3 v3.9.2 h1:mOLFgduk60HFuPmxSix3AluTEh7zhozkby+e1VDo/ro= modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.5 h1:zv111ldxmP7DJ5mOIqzRbza7ZDl3kh4ncKfASB2jIYY= modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sqlite v1.10.6 h1:iNDTQbULcm0IJAqrzCm2JcCqxaKRS94rJ5/clBMRmc8= modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= -modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/tcl v1.5.2 h1:sYNjGr4zK6cDH74USl8wVJRrvDX6UOLpG0j4lFvR0W0= modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= -modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= diff --git a/internal/cmd/commands/database/funcs.go b/internal/cmd/commands/database/funcs.go index 105c5ea1e..fb9c69f83 100644 --- a/internal/cmd/commands/database/funcs.go +++ b/internal/cmd/commands/database/funcs.go @@ -16,7 +16,7 @@ import ( // We expect the database already to be initialized iff initialized is set to true. // Returns a cleanup function which must be called even if an error is returned and // an error code where a non-zero value indicates an error happened. -func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initialized bool) (func(), int) { +func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initialized bool, maxOpenConns int) (func(), int) { noop := func() {} // This database is used to keep an exclusive lock on the database for the // remainder of the command @@ -25,6 +25,7 @@ func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initiali ui.Error(fmt.Errorf("Error establishing db connection: %w", err).Error()) return noop, 2 } + dBase.SetMaxOpenConns(maxOpenConns) if err := dBase.PingContext(ctx); err != nil { ui.Error(fmt.Sprintf("Unable to connect to the database at %q", u)) return noop, 2 diff --git a/internal/cmd/commands/database/funcs_test.go b/internal/cmd/commands/database/funcs_test.go index fbecd15cc..5f115abaf 100644 --- a/internal/cmd/commands/database/funcs_test.go +++ b/internal/cmd/commands/database/funcs_test.go @@ -193,7 +193,7 @@ func TestMigrateDatabase(t *testing.T) { t.Run(tc.name, func(t *testing.T) { u := tc.urlProvider() ui := cli.NewMockUi() - clean, errCode := migrateDatabase(ctx, ui, dialect, u, tc.initialized) + clean, errCode := migrateDatabase(ctx, ui, dialect, u, tc.initialized, 10) clean() assert.EqualValues(t, tc.expectedCode, errCode) assert.Equal(t, tc.expectedOutput, ui.OutputWriter.String()) diff --git a/internal/cmd/commands/database/init.go b/internal/cmd/commands/database/init.go index 19bef5ba8..409748aaa 100644 --- a/internal/cmd/commands/database/init.go +++ b/internal/cmd/commands/database/init.go @@ -239,6 +239,8 @@ func (c *InitCommand) Run(args []string) (retCode int) { return base.CommandUserError } + c.DatabaseMaxOpenConnections = c.Config.Controller.Database.MaxOpenConnections + var migrationUrlToParse string if c.Config.Controller.Database.MigrationUrl != "" { migrationUrlToParse = c.Config.Controller.Database.MigrationUrl @@ -262,7 +264,7 @@ func (c *InitCommand) Run(args []string) (retCode int) { return base.CommandUserError } - clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, false) + clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, false, c.DatabaseMaxOpenConnections) defer clean() switch errCode { case 0: diff --git a/internal/cmd/commands/database/migrate.go b/internal/cmd/commands/database/migrate.go index 19d47210c..63d74de9b 100644 --- a/internal/cmd/commands/database/migrate.go +++ b/internal/cmd/commands/database/migrate.go @@ -259,7 +259,7 @@ plugins { return base.CommandUserError } - clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, true) + clean, errCode := migrateDatabase(c.Context, c.UI, dialect, migrationUrl, true, c.Config.Controller.Database.MaxOpenConnections) defer clean() if errCode != 0 { return errCode diff --git a/internal/credential/vault/client_certificate.go b/internal/credential/vault/client_certificate.go index 057fa29f9..0813f6813 100644 --- a/internal/credential/vault/client_certificate.go +++ b/internal/credential/vault/client_certificate.go @@ -75,7 +75,7 @@ func (c *ClientCertificate) SetTableName(n string) { func (c *ClientCertificate) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { const op = "vault.(ClientCertificate).encrypt" if len(c.CertificateKey) == 0 { - errors.New(ctx, errors.InvalidParameter, op, "no certificate key defined") + return errors.New(ctx, errors.InvalidParameter, op, "no certificate key defined") } if err := structwrapping.WrapStruct(ctx, cipher, c.ClientCertificate, nil); err != nil { return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt)) @@ -86,7 +86,7 @@ func (c *ClientCertificate) encrypt(ctx context.Context, cipher wrapping.Wrapper } c.KeyId = keyId if err := c.hmacCertificateKey(ctx, cipher); err != nil { - errors.Wrap(ctx, err, op) + return errors.Wrap(ctx, err, op) } return nil } diff --git a/internal/credential/vault/client_certificate_test.go b/internal/credential/vault/client_certificate_test.go index 21af5dd26..4e2842379 100644 --- a/internal/credential/vault/client_certificate_test.go +++ b/internal/credential/vault/client_certificate_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/boundary/internal/credential/vault/store" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/stretchr/testify/assert" @@ -49,11 +50,12 @@ func TestClientCertificate_New(t *testing.T) { } tests := []struct { - name string - args args - want *ClientCertificate - wantErr bool - wantEncryptErr bool + name string + args args + want *ClientCertificate + wantErr bool + wantEncryptErr bool + wantEncryptErrCode errors.Code }{ { name: "missing-certificate", @@ -64,7 +66,7 @@ func TestClientCertificate_New(t *testing.T) { wantErr: true, }, { - name: "valid-missing-key", + name: "missing-key", args: args{ certificate: []byte(certPem), }, @@ -73,7 +75,8 @@ func TestClientCertificate_New(t *testing.T) { Certificate: []byte(certPem), }, }, - wantEncryptErr: true, + wantEncryptErr: true, + wantEncryptErrCode: errors.InvalidParameter, }, { name: "valid", @@ -116,10 +119,12 @@ func TestClientCertificate_New(t *testing.T) { err = got.encrypt(ctx, databaseWrapper) if tt.wantEncryptErr { require.Error(err) - } else { - require.NoError(err) - require.NoError(got.decrypt(ctx, databaseWrapper)) + assert.Truef(errors.Match(errors.T(tt.wantEncryptErrCode), err), "%v", err) + return } + + require.NoError(err) + assert.NoError(got.decrypt(ctx, databaseWrapper)) }) } } diff --git a/internal/credential/vault/vault_token.go b/internal/credential/vault/vault_token.go index a0ac6efa5..872981698 100644 --- a/internal/credential/vault/vault_token.go +++ b/internal/credential/vault/vault_token.go @@ -122,7 +122,7 @@ func (t *Token) encrypt(ctx context.Context, cipher wrapping.Wrapper) error { } keyId, err := cipher.KeyId(ctx) if err != nil { - errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error fetching wrapper key id")) + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error fetching wrapper key id")) } t.KeyId = keyId return nil diff --git a/internal/db/testing.go b/internal/db/testing.go index 34fe68e7f..b97c58ff3 100644 --- a/internal/db/testing.go +++ b/internal/db/testing.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/DATA-DOG/go-sqlmock" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file" "github.com/hashicorp/boundary/internal/db/schema" @@ -20,6 +21,7 @@ import ( aead "github.com/hashicorp/go-kms-wrapping/v2/aead" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + pgDriver "gorm.io/driver/postgres" ) // setup the tests (initialize the database one-time and intialized @@ -73,6 +75,23 @@ func TestSetup(t testing.TB, dialect string, opt ...TestOption) (*DB, string) { return db, url } +// TestSetupWithMock will return a test DB and an associated Sqlmock which can +// be used to mock out the db responses. +func TestSetupWithMock(t *testing.T) (*DB, sqlmock.Sqlmock) { + t.Helper() + require := require.New(t) + db, mock, err := sqlmock.New() + require.NoError(err) + require.NoError(err) + dbw, err := dbw.OpenWith(pgDriver.New(pgDriver.Config{ + Conn: db, + })) + require.NoError(err) + return &DB{ + wrapped: dbw, + }, mock +} + // TestWrapper initializes an AEAD wrapping.Wrapper for testing the oplog func TestWrapper(t testing.TB) wrapping.Wrapper { t.Helper() diff --git a/internal/servers/options.go b/internal/servers/options.go index 6d1e274f3..cee422b4b 100644 --- a/internal/servers/options.go +++ b/internal/servers/options.go @@ -1,6 +1,7 @@ package servers import ( + "context" "time" ) @@ -31,10 +32,13 @@ type options struct { withControllerEncryptionPrivateKey []byte withKeyId string withNonce []byte + withNewIdFunc func(context.Context) (string, error) } func getDefaultOptions() options { - return options{} + return options{ + withNewIdFunc: newWorkerId, + } } // WithDescription provides an optional description. @@ -128,3 +132,11 @@ func WithNonce(nonce []byte) Option { o.withNonce = nonce } } + +// WithNewIdFunc allows an optional factory function for new worker IDs to be +// specified (this option is likely only useful for tests). +func WithNewIdFunc(fn func(context.Context) (string, error)) Option { + return func(o *options) { + o.withNewIdFunc = fn + } +} diff --git a/internal/servers/options_test.go b/internal/servers/options_test.go index 2e4e21381..08d05c3d7 100644 --- a/internal/servers/options_test.go +++ b/internal/servers/options_test.go @@ -1,6 +1,9 @@ package servers import ( + "context" + "reflect" + "runtime" "testing" "time" @@ -10,49 +13,60 @@ import ( // Test_GetOpts provides unit tests for GetOpts and all the options func Test_GetOpts(t *testing.T) { t.Parallel() - opts := getOpts() - assert.Equal(t, options{}, opts) - t.Run("WithName", func(t *testing.T) { opts := getOpts(WithName("test")) testOpts := getDefaultOptions() testOpts.withName = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithDescription", func(t *testing.T) { opts := getOpts(WithDescription("test desc")) testOpts := getDefaultOptions() testOpts.withDescription = "test desc" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithLimit", func(t *testing.T) { opts := getOpts(WithLimit(5)) testOpts := getDefaultOptions() testOpts.withLimit = 5 + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithAddress", func(t *testing.T) { opts := getOpts(WithAddress("test")) testOpts := getDefaultOptions() testOpts.withAddress = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithPublicId", func(t *testing.T) { opts := getOpts(WithPublicId("test")) testOpts := getDefaultOptions() testOpts.withPublicId = "test" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithLiveness", func(t *testing.T) { opts := getOpts(WithLiveness(time.Hour)) testOpts := getDefaultOptions() testOpts.withLiveness = time.Hour + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithUpdateTags", func(t *testing.T) { opts := getOpts(WithUpdateTags(true)) testOpts := getDefaultOptions() testOpts.withUpdateTags = true + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithWorkerTags", func(t *testing.T) { @@ -63,6 +77,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithWorkerTags(tags...)) testOpts := getDefaultOptions() testOpts.withWorkerTags = tags + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(t, opts, testOpts) }) t.Run("WithWorkerKeys", func(t *testing.T) { @@ -77,6 +93,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithWorkerKeys(keys)) testOpts = getDefaultOptions() testOpts.withWorkerKeys = keys + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) t.Run("WithControllerEncryptionPrivateKey", func(t *testing.T) { @@ -88,6 +106,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithControllerEncryptionPrivateKey(key)) testOpts = getDefaultOptions() testOpts.withControllerEncryptionPrivateKey = key + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) t.Run("WithKeyId", func(t *testing.T) { @@ -97,6 +117,8 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithKeyId("hi i'm another key id")) testOpts = getDefaultOptions() testOpts.withKeyId = "hi i'm another key id" + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) t.Run("WithNonce", func(t *testing.T) { @@ -108,6 +130,19 @@ func Test_GetOpts(t *testing.T) { opts := getOpts(WithNonce(nonce)) testOpts = getDefaultOptions() testOpts.withNonce = nonce + opts.withNewIdFunc = nil + testOpts.withNewIdFunc = nil assert.Equal(opts, testOpts) }) + t.Run("WithNewIdFunc", func(t *testing.T) { + assert := assert.New(t) + testFn := func(context.Context) (string, error) { return "", nil } + opts := getOpts(WithNewIdFunc(testFn)) + testOpts := getDefaultOptions() + testOpts.withNewIdFunc = testFn + assert.Equal( + runtime.FuncForPC(reflect.ValueOf(opts.withNewIdFunc).Pointer()).Name(), + runtime.FuncForPC(reflect.ValueOf(testOpts.withNewIdFunc).Pointer()).Name(), + ) + }) } diff --git a/internal/servers/repository_worker.go b/internal/servers/repository_worker.go index e2e828362..965f2a020 100644 --- a/internal/servers/repository_worker.go +++ b/internal/servers/repository_worker.go @@ -191,3 +191,51 @@ func setWorkerTags(ctx context.Context, w db.Writer, id string, ts TagSource, ta return nil } + +// CreateWorker will create a worker in the repository and return the written +// worker. Creating a worker is not intentionally oplogged. A worker's +// ReportedStatus and Tags are intentionally ignored when creating a worker (not +// included). Currently, a worker can only be created in the global scope +// +// Options supported: WithNewIdFunc (this option is likely only useful for tests) +func (r *Repository) CreateWorker(ctx context.Context, worker *Worker, opt ...Option) (*Worker, error) { + const op = "servers.CreateWorker" + switch { + case worker == nil: + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing worker") + case worker.PublicId != "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "public id is not empty") + case worker.ScopeId == "": + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + case worker.ScopeId != scope.Global.String(): + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("scope id must be %q", scope.Global.String())) + } + + opts := getOpts(opt...) + var err error + if worker.PublicId, err = opts.withNewIdFunc(ctx); err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to generate worker id")) + } + + var returnedWorker *Worker + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(_ db.Reader, w db.Writer) error { + returnedWorker = worker.clone() + err := w.Create( + ctx, + returnedWorker, + ) + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil + }, + ) + if err != nil { + return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create worker")) + } + return returnedWorker, nil +} diff --git a/internal/servers/repository_worker_test.go b/internal/servers/repository_worker_test.go index a69d474af..2f47b4666 100644 --- a/internal/servers/repository_worker_test.go +++ b/internal/servers/repository_worker_test.go @@ -8,8 +8,11 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/store" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -315,3 +318,153 @@ func TestListWorkersWithLiveness(t *testing.T) { require.Len(result, 3) requireIds([]string{workConf1.WorkerId, workConf2.WorkerId, workConf3.WorkerId}, result) } + +func TestRepository_CreateWorker(t *testing.T) { + t.Parallel() + testCtx := context.Background() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + testRepo, err := servers.NewRepository(rw, rw, kms) + require.NoError(t, err) + + iamRepo := iam.TestRepo(t, conn, wrapper) + org, _ := iam.TestScopes(t, iamRepo) + + testNewIdFn := func(context.Context) (string, error) { + return "", errors.New(testCtx, errors.Internal, "test", "testNewIdFn-error") + } + + tests := []struct { + name string + setup func() *servers.Worker + repo *servers.Repository + opt []servers.Option + wantErr bool + wantErrIs errors.Code + wantErrContains string + }{ + { + name: "missing-worker", + setup: func() *servers.Worker { + return nil + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing worker", + }, + { + name: "public-id-not-empty", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + var err error + w.PublicId, err = db.NewPublicId(servers.WorkerPrefix) + require.NoError(t, err) + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "public id is not empty", + }, + { + name: "empty-scope-id", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + w.ScopeId = "" + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "missing scope id", + }, + { + name: "org-scope-id", + setup: func() *servers.Worker { + w := servers.NewWorker(org.PublicId) + return w + }, + repo: testRepo, + wantErr: true, + wantErrIs: errors.InvalidParameter, + wantErrContains: "scope id must be \"global\"", + }, + { + name: "new-id-error", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + repo: testRepo, + opt: []servers.Option{servers.WithNewIdFunc(testNewIdFn)}, + wantErr: true, + wantErrIs: errors.Internal, + wantErrContains: "testNewIdFn-error", + }, + { + name: "create-error", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + repo: func() *servers.Repository { + conn, mock := db.TestSetupWithMock(t) + writer := db.New(conn) + mock.ExpectBegin() + mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(testCtx, errors.Internal, "test", "create-error")) + mock.ExpectRollback() + r, err := servers.NewRepository(rw, writer, kms) + require.NoError(t, err) + return r + }(), + wantErr: true, + wantErrContains: "unable to create worker", + }, + { + name: "success", + setup: func() *servers.Worker { + w := servers.NewWorker(scope.Global.String()) + return w + }, + repo: testRepo, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + testWorker := tc.setup() + got, err := tc.repo.CreateWorker(testCtx, testWorker, tc.opt...) + if tc.wantErr { + require.Error(err) + assert.Nil(got) + if tc.wantErrIs != errors.Unknown { + assert.True(errors.Match(errors.T(tc.wantErrIs), err)) + } + if tc.wantErrContains != "" { + assert.Contains(err.Error(), tc.wantErrContains) + } + return + } + require.NoError(err) + assert.NotNil(got) + assert.NotEmpty(got.PublicId) + assert.NotEmpty(got.UpdateTime) + assert.NotEmpty(got.CreateTime) + + found := &servers.Worker{ + Worker: &store.Worker{ + PublicId: got.PublicId, + }, + } + err = rw.LookupByPublicId(testCtx, found) + require.NoError(err) + if found.Tags == nil { + found.Tags = []*servers.Tag{} + } + assert.Equal(got, found) + }) + } +} diff --git a/internal/tests/api/targets/target_test.go b/internal/tests/api/targets/target_test.go index c7966c319..835b00f71 100644 --- a/internal/tests/api/targets/target_test.go +++ b/internal/tests/api/targets/target_test.go @@ -179,11 +179,11 @@ func TestList(t *testing.T) { require.NoError(err) expected[i] = tcr.Item } + filterItem := expected[3] ul, err = tarClient.List(tc.Context(), proj.GetPublicId()) require.NoError(err) assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul.Items)) - filterItem := ul.Items[3] ul, err = tarClient.List(tc.Context(), proj.GetPublicId(), targets.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id))) require.NoError(err) diff --git a/internal/ui/VERSION b/internal/ui/VERSION index e1c104f63..c5193b209 100644 --- a/internal/ui/VERSION +++ b/internal/ui/VERSION @@ -1,4 +1,4 @@ -8545697b430a432bd50c1bde6a080759f803f157 Add toolbar regions to align refresh button to the right (#1098) +698160d423e8fee640d3286c3a3f17434f56a12b # This file determines the version of the UI to embed in the boundary binary. # Update this file by running 'make update-ui-version' from the root of this repo. # Set UI_COMMITISH when running the above target to update to a specific version.