From 8d5ec21d73ef6fe4f1792c6da9ba71b84f5dfa3d Mon Sep 17 00:00:00 2001 From: Todd Date: Tue, 12 Mar 2024 13:04:20 -0700 Subject: [PATCH] Create aliases when creating a target in repo (#4504) * Create aliases when creating a target in repo --- internal/alias/target/alias.go | 2 +- internal/alias/target/repository_alias.go | 10 ++-- internal/target/options.go | 9 ++++ internal/target/options_test.go | 11 ++++ internal/target/repository.go | 27 ++++++++-- .../target/tcp/repository_tcp_target_test.go | 54 +++++++++++++++++++ 6 files changed, 104 insertions(+), 9 deletions(-) diff --git a/internal/alias/target/alias.go b/internal/alias/target/alias.go index 8d4c68cb92..8f07964b95 100644 --- a/internal/alias/target/alias.go +++ b/internal/alias/target/alias.go @@ -20,7 +20,7 @@ type Alias struct { tableName string `gorm:"-"` } -func (al *Alias) clone() *Alias { +func (al *Alias) Clone() *Alias { cp := proto.Clone(al.Alias) return &Alias{ Alias: cp.(*store.Alias), diff --git a/internal/alias/target/repository_alias.go b/internal/alias/target/repository_alias.go index cb2e281257..1b21338b86 100644 --- a/internal/alias/target/repository_alias.go +++ b/internal/alias/target/repository_alias.go @@ -38,7 +38,7 @@ func (r *Repository) CreateAlias(ctx context.Context, a *Alias, opt ...Option) ( case a.PublicId != "": return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") } - a = a.clone() + a = a.Clone() id, err := newAliasId(ctx) if err != nil { @@ -59,7 +59,7 @@ func (r *Repository) CreateAlias(ctx context.Context, a *Alias, opt ...Option) ( db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error { - newAlias = a.clone() + newAlias = a.Clone() err := w.Create( ctx, newAlias, @@ -140,7 +140,7 @@ func (r *Repository) UpdateAlias(ctx context.Context, a *Alias, version uint32, return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } - a = a.clone() + a = a.Clone() metadata := newAliasMetadata(a, oplog.OpType_OP_TYPE_UPDATE) @@ -151,7 +151,7 @@ func (r *Repository) UpdateAlias(ctx context.Context, a *Alias, version uint32, db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error { - returnedAlias = a.clone() + returnedAlias = a.Clone() var err error rowsUpdated, err = w.Update( ctx, @@ -257,7 +257,7 @@ func (r *Repository) DeleteAlias(ctx context.Context, id string, opt ...Option) db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error { - deleteAlias = a.clone() + deleteAlias = a.Clone() var err error rowsDeleted, err = w.Delete( ctx, diff --git a/internal/target/options.go b/internal/target/options.go index 7ad93edc7e..fc7a62e50e 100644 --- a/internal/target/options.go +++ b/internal/target/options.go @@ -8,6 +8,7 @@ import ( "time" "github.com/hashicorp/boundary/globals" + talias "github.com/hashicorp/boundary/internal/alias/target" intglobals "github.com/hashicorp/boundary/internal/globals" "github.com/hashicorp/boundary/internal/pagination" "github.com/hashicorp/boundary/internal/perms" @@ -54,6 +55,7 @@ type options struct { WithEnableSessionRecording bool WithNetResolver intglobals.NetIpResolver WithStartPageAfterItem pagination.Item + withAliases []*talias.Alias } func getDefaultOptions() options { @@ -275,3 +277,10 @@ func WithStartPageAfterItem(item pagination.Item) Option { o.WithStartPageAfterItem = item } } + +// WithAliases provides an option to provide aliases. +func WithAliases(in []*talias.Alias) Option { + return func(o *options) { + o.withAliases = in + } +} diff --git a/internal/target/options_test.go b/internal/target/options_test.go index 7e0a72985c..1069ba80e9 100644 --- a/internal/target/options_test.go +++ b/internal/target/options_test.go @@ -4,16 +4,19 @@ package target import ( + "context" "testing" "time" "github.com/hashicorp/boundary/globals" + talias "github.com/hashicorp/boundary/internal/alias/target" "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/pagination" "github.com/hashicorp/boundary/internal/perms" "github.com/hashicorp/boundary/internal/target/store" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type fakeItem struct { @@ -257,4 +260,12 @@ func Test_GetOpts(t *testing.T) { assert.Equal(opts.WithStartPageAfterItem.GetPublicId(), "s_1") assert.Equal(opts.WithStartPageAfterItem.GetUpdateTime(), timestamp.New(updateTime)) }) + t.Run("WithAliases", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + al, err := talias.NewAlias(context.Background(), "global", "test") + require.NoError(err) + input := []*talias.Alias{al} + opts := GetOpts(WithAliases(input)) + assert.Equal(input, opts.withAliases) + }) } diff --git a/internal/target/repository.go b/internal/target/repository.go index c9d394b334..1b27afb32c 100644 --- a/internal/target/repository.go +++ b/internal/target/repository.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/hashicorp/boundary/globals" talias "github.com/hashicorp/boundary/internal/alias/target" "github.com/hashicorp/boundary/internal/boundary" "github.com/hashicorp/boundary/internal/db" @@ -503,7 +504,7 @@ func (r *Repository) DeleteTarget(ctx context.Context, publicId string, _ ...Opt // CreateTarget inserts into the repository and returns the new Target with // its list of host sets and credential libraries. -// WithPublicId is the only supported option. +// WithPublicId and WithAliases are supported options. func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Option) (Target, error) { const op = "target.(Repository).CreateTarget" opts := GetOpts(opt...) @@ -562,7 +563,8 @@ func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Opt } metadata := t.Oplog(oplog.OpType_OP_TYPE_CREATE) - var returnedTarget any + var createdAliases []*talias.Alias + var returnedTarget Target _, err = r.writer.DoTx( ctx, db.StdRetryCnt, @@ -588,6 +590,24 @@ func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Opt msgs = append(msgs, &targetAddressOplogMsg) } + createdAliases = make([]*talias.Alias, 0, len(opts.withAliases)) + for _, a := range opts.withAliases { + a = a.Clone() + aliasId, err := db.NewPublicId(ctx, globals.TargetAliasPrefix) + if err != nil { + return errors.Wrap(ctx, err, op) + } + a.PublicId = aliasId + + a.DestinationId = t.GetPublicId() + var targetAliasOplogMsg oplog.Message + if err := w.Create(ctx, a, db.NewOplogMsg(&targetAliasOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target alias")) + } + createdAliases = append(createdAliases, a) + msgs = append(msgs, &targetAliasOplogMsg) + } + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) } @@ -598,7 +618,8 @@ func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Opt if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s target id", t.GetPublicId()))) } - return returnedTarget.(Target), nil + returnedTarget.SetAliases(createdAliases) + return returnedTarget, nil } // UpdateTarget will update a target in the repository and return the written diff --git a/internal/target/tcp/repository_tcp_target_test.go b/internal/target/tcp/repository_tcp_target_test.go index 692765116d..5f944351b1 100644 --- a/internal/target/tcp/repository_tcp_target_test.go +++ b/internal/target/tcp/repository_tcp_target_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/hashicorp/boundary/globals" + talias "github.com/hashicorp/boundary/internal/alias/target" "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" @@ -251,6 +252,59 @@ func TestRepository_CreateTarget(t *testing.T) { // add an appropriate assert. }) } + + t.Run("create with aliases", func(t *testing.T) { + newAlias1, err := talias.NewAlias(context.Background(), "global", "alias1", talias.WithHostId("hst_1234567890")) + require.NoError(t, err) + newAlias2, err := talias.NewAlias(context.Background(), "global", "alias2", talias.WithHostId("hst_0987654321")) + require.NoError(t, err) + + tar, err := target.New(ctx, tcp.Subtype, proj.PublicId, + target.WithName("create-with-alias"), + target.WithDescription("create-with-alias"), + target.WithDefaultPort(uint32(22))) + require.NoError(t, err) + tar, err = repo.CreateTarget(context.Background(), tar, target.WithAliases([]*talias.Alias{newAlias1, newAlias2})) + require.NoError(t, err) + assert.NotNil(t, tar) + assert.Len(t, tar.GetAliases(), 2) + assert.NotEmpty(t, tar.GetAliases()[0].GetPublicId()) + assert.NotEmpty(t, tar.GetAliases()[1].GetPublicId()) + assert.NotZero(t, tar.GetAliases()[0].GetCreateTime()) + assert.NotZero(t, tar.GetAliases()[1].GetCreateTime()) + assert.Contains(t, []string{newAlias1.GetValue(), newAlias2.GetValue()}, tar.GetAliases()[0].GetValue()) + assert.Contains(t, []string{newAlias1.GetValue(), newAlias2.GetValue()}, tar.GetAliases()[1].GetValue()) + assert.Contains(t, []string{newAlias1.GetHostId(), newAlias2.GetHostId()}, tar.GetAliases()[0].GetHostId()) + assert.Contains(t, []string{newAlias1.GetHostId(), newAlias2.GetHostId()}, tar.GetAliases()[1].GetHostId()) + }) + + t.Run("create an invalid aliases", func(t *testing.T) { + invalidAlias, err := talias.NewAlias(context.Background(), "global", "invalid_alias") + require.NoError(t, err) + + tar, err := target.New(ctx, tcp.Subtype, proj.PublicId, + target.WithName("create-with-invalid-alias"), + target.WithDescription("create-with-invalid-alias"), + target.WithDefaultPort(uint32(22))) + require.NoError(t, err) + tar, err = repo.CreateTarget(context.Background(), tar, target.WithAliases([]*talias.Alias{invalidAlias})) + require.Error(t, err) + require.Nil(t, tar) + }) + + t.Run("create with duplicate aliases", func(t *testing.T) { + dupAlias, err := talias.NewAlias(context.Background(), "global", "dup-alias") + require.NoError(t, err) + + tar, err := target.New(ctx, tcp.Subtype, proj.PublicId, + target.WithName("create-with-duplicate-alias"), + target.WithDescription("create-with-duplicate-alias"), + target.WithDefaultPort(uint32(22))) + require.NoError(t, err) + tar, err = repo.CreateTarget(context.Background(), tar, target.WithAliases([]*talias.Alias{dupAlias, dupAlias})) + require.Error(t, err) + require.Nil(t, tar) + }) } func TestRepository_UpdateTcpTarget(t *testing.T) {