feat(bsr): populate container meta from meta files
This commit is contained in:
1 parent
4e5401da7d
commit
d06c9b2933
6 files changed
+324
-13
No files matched your search
+254
-11
@@ -4,10 +4,13 @@
|
||||
package bsr
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/bsr/internal/checksum"
|
||||
"github.com/hashicorp/boundary/internal/bsr/internal/is"
|
||||
@@ -30,6 +33,159 @@ const (
|
||||
pubKeySelfSignatureFile = "pubKeySelfSignature.sign"
|
||||
)
|
||||
|
||||
// decodeSessionMeta will populate a SessionMeta for an opened BSR Session
|
||||
// TODO Unmarshal without brute force
|
||||
func decodeSessionMeta(ctx context.Context, r io.Reader) (*SessionMeta, error) {
|
||||
const op = "bsr.decodeSessionMeta"
|
||||
|
||||
switch {
|
||||
case r == nil:
|
||||
return nil, fmt.Errorf("%s: missing session meta file: %w", op, ErrInvalidParameter)
|
||||
}
|
||||
|
||||
s := &SessionMeta{}
|
||||
user := &User{Scope: Scope{}}
|
||||
target := &Target{Scope: Scope{}}
|
||||
sHost := &StaticHost{Catalog: StaticHostCatalog{}}
|
||||
dHost := &DynamicHost{Catalog: DynamicHostCatalog{}}
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
l := scanner.Text()
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(l, ":")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: session meta file contains invalid entry; expecting k: v pair:%s", op, l)
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
switch {
|
||||
case k == "id":
|
||||
s.Id = v
|
||||
case k == "protocol":
|
||||
s.Protocol = Protocol(v)
|
||||
|
||||
// User
|
||||
case k == "user_publicId":
|
||||
user.PublicId = v
|
||||
case k == "user_name":
|
||||
user.Name = v
|
||||
case k == "user_description":
|
||||
user.Description = v
|
||||
case k == "user_scope_publicId":
|
||||
user.Scope.PublicId = v
|
||||
case k == "user_scope_name":
|
||||
user.Scope.Name = v
|
||||
case k == "user_scope_description":
|
||||
user.Scope.Description = v
|
||||
case k == "user_scope_type":
|
||||
user.Scope.Type = v
|
||||
case k == "user_scope_parentId":
|
||||
user.Scope.ParentId = v
|
||||
case k == "user_scope_primaryAuthMethod":
|
||||
user.Scope.PrimaryAuthMethodId = v
|
||||
|
||||
// Target
|
||||
case k == "target_publicId":
|
||||
target.PublicId = v
|
||||
case k == "target_projectId":
|
||||
target.ProjectId = v
|
||||
case k == "target_name":
|
||||
target.Name = v
|
||||
case k == "target_description":
|
||||
target.Description = v
|
||||
case k == "target_defaultPort":
|
||||
dp, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.DefaultPort = uint32(dp)
|
||||
case k == "target_sessionMaxSeconds":
|
||||
sms, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.SessionMaxSeconds = uint32(sms)
|
||||
case k == "target_sessionConnectionLimit":
|
||||
scl, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.SessionConnectionLimit = int32(scl)
|
||||
case k == "target_workerFilter":
|
||||
target.WorkerFilter = v
|
||||
case k == "target_egressWorkerFilter":
|
||||
target.EgressWorkerFilter = v
|
||||
case k == "target_ingressWorkerFilter":
|
||||
target.IngressWorkerFilter = v
|
||||
case k == "target_scope_publicId":
|
||||
target.Scope.PublicId = v
|
||||
case k == "target_scope_name":
|
||||
target.Scope.Name = v
|
||||
case k == "target_scope_description":
|
||||
target.Scope.Description = v
|
||||
case k == "target_scope_type":
|
||||
target.Scope.Type = v
|
||||
case k == "target_scope_parentId":
|
||||
target.Scope.ParentId = v
|
||||
case k == "target_scope_primaryAuthMethod":
|
||||
target.Scope.PrimaryAuthMethodId = v
|
||||
|
||||
// Static Host
|
||||
case k == "staticHost_publicId":
|
||||
sHost.PublicId = v
|
||||
case k == "staticHost_name":
|
||||
sHost.Name = v
|
||||
case k == "staticHost_description":
|
||||
sHost.Description = v
|
||||
case k == "staticHost_address":
|
||||
sHost.Address = v
|
||||
case k == "staticHostCatalog_publicId":
|
||||
sHost.Catalog.PublicId = v
|
||||
case k == "staticHostCatalog_projectId":
|
||||
sHost.Catalog.ProjectId = v
|
||||
case k == "staticHostCatalog_name":
|
||||
sHost.Catalog.Name = v
|
||||
case k == "staticHostCatalog_description":
|
||||
sHost.Catalog.Description = v
|
||||
|
||||
// Dynamic Host
|
||||
case k == "dynamicHost_publicId":
|
||||
dHost.PublicId = v
|
||||
case k == "dynamicHost_name":
|
||||
dHost.Name = v
|
||||
case k == "dynamicHost_description":
|
||||
dHost.Description = v
|
||||
case k == "dynamicHost_externalId":
|
||||
dHost.ExternalId = v
|
||||
case k == "dynamicHostCatalog_publicId":
|
||||
dHost.Catalog.PublicId = v
|
||||
case k == "dynamicHostCatalog_projectId":
|
||||
dHost.Catalog.ProjectId = v
|
||||
case k == "dynamicHostCatalog_name":
|
||||
dHost.Catalog.Name = v
|
||||
case k == "dynamicHostCatalog_description":
|
||||
dHost.Catalog.Description = v
|
||||
case k == "dynamicHostCatalog_pluginId":
|
||||
dHost.Catalog.PluginId = v
|
||||
case k == "dynamicHostCatalog_attributes":
|
||||
dHost.Catalog.Attributes = v
|
||||
}
|
||||
}
|
||||
|
||||
s.Target = target
|
||||
s.User = user
|
||||
if sHost.PublicId != "" {
|
||||
s.StaticHost = sHost
|
||||
}
|
||||
if dHost.PublicId != "" {
|
||||
s.DynamicHost = dHost
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Session is the top level container in a bsr that contains the files for
|
||||
// a recorded session.
|
||||
type Session struct {
|
||||
@@ -150,6 +306,51 @@ func OpenSession(ctx context.Context, sessionRecordingId string, f storage.FS, k
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// ConnectionMeta contains metadata about a connection in a BSR.
|
||||
type ConnectionMeta struct {
|
||||
Id string
|
||||
}
|
||||
|
||||
func (c ConnectionMeta) isValid() bool {
|
||||
switch {
|
||||
case c.Id == "":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// decodeConnectionMeta will populate the ConnectionMeta for a BSR Connection
|
||||
// TODO Unmarshal without brute force
|
||||
func decodeConnectionMeta(ctx context.Context, r io.Reader) (*ConnectionMeta, error) {
|
||||
const op = "bsr.decodeConnectionMeta"
|
||||
|
||||
switch {
|
||||
case r == nil:
|
||||
return nil, fmt.Errorf("%s: missing connection meta file: %w", op, ErrInvalidParameter)
|
||||
}
|
||||
|
||||
c := &ConnectionMeta{}
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
l := scanner.Text()
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(l, ":")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: connection meta file contains invalid entry; expecting k: v pair:%s", op, l)
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
switch {
|
||||
case k == "id":
|
||||
c.Id = v
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewConnection creates a Connection container for a given connection id.
|
||||
func (s *Session) NewConnection(ctx context.Context, meta *ConnectionMeta) (*Connection, error) {
|
||||
const op = "bsr.(Session).NewConnection"
|
||||
@@ -189,11 +390,6 @@ func (s *Session) Close(ctx context.Context) error {
|
||||
return s.container.close(ctx)
|
||||
}
|
||||
|
||||
// ConnectionMeta contains metadata about a connection in a BSR.
|
||||
type ConnectionMeta struct {
|
||||
Id string
|
||||
}
|
||||
|
||||
// Connection is a container in a bsr for a specific connection in a session
|
||||
// container. It contains the files for the recorded connection.
|
||||
type Connection struct {
|
||||
@@ -203,6 +399,56 @@ type Connection struct {
|
||||
Meta *ConnectionMeta
|
||||
}
|
||||
|
||||
// ChannelMeta contains metadata about a channel in a BSR.
|
||||
type ChannelMeta struct {
|
||||
Id string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (c ChannelMeta) isValid() bool {
|
||||
switch {
|
||||
case c.Id == "":
|
||||
return false
|
||||
case c.Type == "":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// decodeChannelMeta will populate the ChannelMeta for a BSR Channel
|
||||
// TODO Unmarshal without brute force
|
||||
func decodeChannelMeta(ctx context.Context, r io.Reader) (*ChannelMeta, error) {
|
||||
const op = "bsr.decodeChannelMeta"
|
||||
|
||||
switch {
|
||||
case r == nil:
|
||||
return nil, fmt.Errorf("%s: missing channel meta file: %w", op, ErrInvalidParameter)
|
||||
}
|
||||
|
||||
c := &ChannelMeta{}
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
l := scanner.Text()
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(l, ":")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: channel meta file contains invalid entry; expecting k: v pair:%s", op, l)
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
switch {
|
||||
case k == "id":
|
||||
c.Id = v
|
||||
case k == "channelType":
|
||||
c.Type = v
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewChannel creates a Channel container for a given channel id.
|
||||
func (c *Connection) NewChannel(ctx context.Context, meta *ChannelMeta) (*Channel, error) {
|
||||
const op = "bsr.(Connection).NewChannel"
|
||||
@@ -233,6 +479,9 @@ func (c *Connection) NewChannel(ctx context.Context, meta *ChannelMeta) (*Channe
|
||||
if _, err := nc.WriteMeta(ctx, "id", meta.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := nc.WriteMeta(ctx, "channelType", meta.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Channel{
|
||||
container: nc,
|
||||
Meta: meta,
|
||||
@@ -288,12 +537,6 @@ func (c *Connection) Close(ctx context.Context) error {
|
||||
return c.container.close(ctx)
|
||||
}
|
||||
|
||||
// ChannelMeta contains metadata about a channel in a BSR.
|
||||
type ChannelMeta struct {
|
||||
Id string
|
||||
Type string
|
||||
}
|
||||
|
||||
// Channel is a container in a bsr for a specific channel in a session
|
||||
// container. It contains the files for the recorded channel.
|
||||
type Channel struct {
|
||||
|
||||
@@ -282,7 +282,7 @@ func TestBsr(t *testing.T) {
|
||||
|
||||
createdChannels := make([]*channel, 0, len(conn.channels))
|
||||
for _, chann := range conn.channels {
|
||||
ch, err := c.NewChannel(ctx, &bsr.ChannelMeta{Id: chann.id})
|
||||
ch, err := c.NewChannel(ctx, &bsr.ChannelMeta{Id: chann.id, Type: "chan"})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ch)
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package bsr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/bsr/internal/fstest"
|
||||
"github.com/hashicorp/boundary/internal/bsr/kms"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPopulateMeta(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
keys, err := kms.CreateKeys(ctx, kms.TestWrapper(t), "session")
|
||||
require.NoError(t, err)
|
||||
|
||||
sessionId := "session"
|
||||
sessionProtocol := "SSH"
|
||||
|
||||
// Populate session meta
|
||||
sessionMeta := TestSessionMeta(sessionId, Protocol((sessionProtocol)))
|
||||
s, err := NewSession(ctx, sessionMeta, &fstest.MemFS{}, keys, WithSupportsMultiplex(true))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s)
|
||||
|
||||
// Reset meta and populate it from the meta file
|
||||
s.Meta = &SessionMeta{}
|
||||
sm, err := decodeSessionMeta(ctx, s.container.meta)
|
||||
require.NoError(t, err)
|
||||
s.Meta = sm
|
||||
require.Equal(t, s.Meta.Id, sessionId)
|
||||
require.Equal(t, s.Meta.Protocol.ToText(), sessionProtocol)
|
||||
require.Equal(t, s.Meta.Target, sessionMeta.Target)
|
||||
require.Equal(t, s.Meta.User, sessionMeta.User)
|
||||
require.Equal(t, s.Meta.StaticHost, sessionMeta.StaticHost)
|
||||
|
||||
// Populate connection meta
|
||||
connectionId := "connection"
|
||||
c, err := s.NewConnection(ctx, &ConnectionMeta{Id: connectionId})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, c)
|
||||
|
||||
c.Meta = &ConnectionMeta{}
|
||||
cm, err := decodeConnectionMeta(ctx, c.container.meta)
|
||||
require.NoError(t, err)
|
||||
c.Meta = cm
|
||||
require.Equal(t, c.Meta.Id, connectionId)
|
||||
|
||||
// Populate channel meta
|
||||
channelId := "channel"
|
||||
channelType := "mythical"
|
||||
ch, err := c.NewChannel(ctx, &ChannelMeta{Id: channelId, Type: channelType})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ch)
|
||||
|
||||
ch.Meta = &ChannelMeta{}
|
||||
chM, err := decodeChannelMeta(ctx, ch.container.meta)
|
||||
require.NoError(t, err)
|
||||
ch.Meta = chM
|
||||
require.Equal(t, ch.Meta.Id, channelId)
|
||||
require.Equal(t, ch.Meta.Type, channelType)
|
||||
}
|
||||
+1
-1
@@ -2,5 +2,5 @@ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 messages-inbou
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-inbound.data
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 messages-outbound.data
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 requests-outbound.data
|
||||
e30a207b53f444c7bd2d1e8cc102abf910dfafd70aaef7f24db00298832b74d3 channel.meta
|
||||
27a58452605f6e30ca048cc540e9d963c7270212fcb2c7b73fc69e314ffd1902 channel.meta
|
||||
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 channel.summary
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
id: chan_1
|
||||
channelType: chan
|
||||
messages: inbound
|
||||
requests: inbound
|
||||
messages: outbound
|
||||
|
||||
+1
@@ -1 +1,2 @@
|
||||
id: chan_1
|
||||
channelType: chan
|
||||
Reference in new issue
Block a user