diff --git a/api/sessionrecordings/channel_recording.gen.go b/api/sessionrecordings/channel_recording.gen.go new file mode 100644 index 0000000000..b6dae33898 --- /dev/null +++ b/api/sessionrecordings/channel_recording.gen.go @@ -0,0 +1,16 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +import ( + "time" +) + +type ChannelRecording struct { + Id string `json:"id,omitempty"` + BytesUp uint64 `json:"bytes_up,omitempty"` + BytesDown uint64 `json:"bytes_down,omitempty"` + StartTime time.Time `json:"start_time,omitempty"` + EndTime time.Time `json:"end_time,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + MimeTypes []string `json:"mime_types,omitempty"` +} diff --git a/api/sessionrecordings/connection_recording.gen.go b/api/sessionrecordings/connection_recording.gen.go new file mode 100644 index 0000000000..3bb245496b --- /dev/null +++ b/api/sessionrecordings/connection_recording.gen.go @@ -0,0 +1,18 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +import ( + "time" +) + +type ConnectionRecording struct { + Id string `json:"id,omitempty"` + ConnectionId string `json:"connection_id,omitempty"` + BytesUp uint64 `json:"bytes_up,omitempty"` + BytesDown uint64 `json:"bytes_down,omitempty"` + StartTime time.Time `json:"start_time,omitempty"` + EndTime time.Time `json:"end_time,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + MimeTypes []string `json:"mime_types,omitempty"` + ChannelRecordings []*ChannelRecording `json:"channel_recordings,omitempty"` +} diff --git a/api/sessionrecordings/custom.go b/api/sessionrecordings/custom.go new file mode 100644 index 0000000000..2835ff75ec --- /dev/null +++ b/api/sessionrecordings/custom.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package sessionrecordings + +import ( + "context" + "fmt" + "io" + "net/url" +) + +const chunkSize = 64 * 1024 // assume we're using 64 KiB see: https://github.com/grpc/grpc.github.io/issues/371 + +func (c *Client) Download(ctx context.Context, contentId string, opt ...Option) (io.ReadCloser, error) { + switch { + case contentId == "": + return nil, fmt.Errorf("empty content id value passed into download request") + case c.client == nil: + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + req, err := c.client.NewRequest(ctx, "GET", "session_recordings/"+url.PathEscape(contentId)+":download", nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating download request: %w", err) + } + req.Header.Set("Accept", "application/x-asciicast") + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during download call: %w", err) + } + return resp.HttpResponse().Body, nil +} diff --git a/api/sessionrecordings/option.gen.go b/api/sessionrecordings/option.gen.go new file mode 100644 index 0000000000..591076b87c --- /dev/null +++ b/api/sessionrecordings/option.gen.go @@ -0,0 +1,68 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package sessionrecordings + +import ( + "strings" + + "github.com/hashicorp/boundary/api" +) + +// Option is a func that sets optional attributes for a call. This does not need +// to be used directly, but instead option arguments are built from the +// functions in this package. WithX options set a value to that given in the +// argument; DefaultX options indicate that the value should be set to its +// default. When an API call is made options are processed in ther order they +// appear in the function call, so for a given argument X, a succession of WithX +// or DefaultX calls will result in the last call taking effect. +type Option func(*options) + +type options struct { + postMap map[string]interface{} + queryMap map[string]string + withAutomaticVersioning bool + withSkipCurlOutput bool + withFilter string +} + +func getDefaultOptions() options { + return options{ + postMap: make(map[string]interface{}), + queryMap: make(map[string]string), + } +} + +func getOpts(opt ...Option) (options, []api.Option) { + opts := getDefaultOptions() + for _, o := range opt { + if o != nil { + o(&opts) + } + } + var apiOpts []api.Option + if opts.withSkipCurlOutput { + apiOpts = append(apiOpts, api.WithSkipCurlOutput(true)) + } + if opts.withFilter != "" { + opts.queryMap["filter"] = opts.withFilter + } + return opts, apiOpts +} + +// WithSkipCurlOutput tells the API to not use the current call for cURL output. +// Useful for when we need to look up versions. +func WithSkipCurlOutput(skip bool) Option { + return func(o *options) { + o.withSkipCurlOutput = true + } +} + +// WithFilter tells the API to filter the items returned using the provided +// filter term. The filter should be in a format supported by +// hashicorp/go-bexpr. +func WithFilter(filter string) Option { + return func(o *options) { + o.withFilter = strings.TrimSpace(filter) + } +} diff --git a/api/sessionrecordings/session_recording.gen.go b/api/sessionrecordings/session_recording.gen.go new file mode 100644 index 0000000000..e6cf068943 --- /dev/null +++ b/api/sessionrecordings/session_recording.gen.go @@ -0,0 +1,158 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +import ( + "context" + "fmt" + "net/url" + "time" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/scopes" +) + +type SessionRecording struct { + Id string `json:"id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + SessionId string `json:"session_id,omitempty"` + StorageBucketId string `json:"storage_bucket_id,omitempty"` + BytesUp uint64 `json:"bytes_up,omitempty"` + BytesDown uint64 `json:"bytes_down,omitempty"` + StartTime time.Time `json:"start_time,omitempty"` + EndTime time.Time `json:"end_time,omitempty"` + Duration time.Duration `json:"duration,omitempty"` + DeleteOn time.Time `json:"delete_on,omitempty"` + Type string `json:"type,omitempty"` + MimeTypes []string `json:"mime_types,omitempty"` + ConnectionRecordings []*ConnectionRecording `json:"connection_recordings,omitempty"` + CreateTimeValues *ValuesAtTime `json:"create_time_values,omitempty"` + AuthorizedActions []string `json:"authorized_actions,omitempty"` + + response *api.Response +} + +type SessionRecordingReadResult struct { + Item *SessionRecording + response *api.Response +} + +func (n SessionRecordingReadResult) GetItem() *SessionRecording { + return n.Item +} + +func (n SessionRecordingReadResult) GetResponse() *api.Response { + return n.response +} + +type SessionRecordingListResult struct { + Items []*SessionRecording + response *api.Response +} + +func (n SessionRecordingListResult) GetItems() []*SessionRecording { + return n.Items +} + +func (n SessionRecordingListResult) GetResponse() *api.Response { + return n.response +} + +// Client is a client for this collection +type Client struct { + client *api.Client +} + +// Creates a new client for this collection. The submitted API client is cloned; +// modifications to it after generating this client will not have effect. If you +// need to make changes to the underlying API client, use ApiClient() to access +// it. +func NewClient(c *api.Client) *Client { + return &Client{client: c.Clone()} +} + +// ApiClient returns the underlying API client +func (c *Client) ApiClient() *api.Client { + return c.client +} + +func (c *Client) Read(ctx context.Context, id string, opt ...Option) (*SessionRecordingReadResult, error) { + if id == "" { + return nil, fmt.Errorf("empty id value passed into Read request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + + req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("session-recordings/%s", url.PathEscape(id)), nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating Read request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error performing client request during Read call: %w", err) + } + + target := new(SessionRecordingReadResult) + target.Item = new(SessionRecording) + apiErr, err := resp.Decode(target.Item) + if err != nil { + return nil, fmt.Errorf("error decoding Read response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} + +func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*SessionRecordingListResult, error) { + if scopeId == "" { + return nil, fmt.Errorf("empty scopeId value passed into List request") + } + if c.client == nil { + return nil, fmt.Errorf("nil client") + } + + opts, apiOpts := getOpts(opt...) + opts.queryMap["scope_id"] = scopeId + + req, err := c.client.NewRequest(ctx, "GET", "session-recordings", nil, apiOpts...) + if err != nil { + return nil, fmt.Errorf("error creating List request: %w", err) + } + + if len(opts.queryMap) > 0 { + q := url.Values{} + for k, v := range opts.queryMap { + q.Add(k, v) + } + req.URL.RawQuery = q.Encode() + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("error performing client request during List call: %w", err) + } + + target := new(SessionRecordingListResult) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, fmt.Errorf("error decoding List response: %w", err) + } + if apiErr != nil { + return nil, apiErr + } + target.response = resp + return target, nil +} diff --git a/api/sessionrecordings/ssh_target_attributes.gen.go b/api/sessionrecordings/ssh_target_attributes.gen.go new file mode 100644 index 0000000000..d5404a42fc --- /dev/null +++ b/api/sessionrecordings/ssh_target_attributes.gen.go @@ -0,0 +1,6 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +type SshTargetAttributes struct { + DefaultPort uint32 `json:"default_port,omitempty"` +} diff --git a/api/sessionrecordings/target.gen.go b/api/sessionrecordings/target.gen.go new file mode 100644 index 0000000000..2ecea4a795 --- /dev/null +++ b/api/sessionrecordings/target.gen.go @@ -0,0 +1,19 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +import ( + "github.com/hashicorp/boundary/api/scopes" +) + +type Target struct { + Id string `json:"id,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + SessionMaxSeconds uint32 `json:"session_max_seconds,omitempty"` + SessionConnectionLimit int32 `json:"session_connection_limit,omitempty"` + WorkerFilter string `json:"worker_filter,omitempty"` + EgressWorkerFilter string `json:"egress_worker_filter,omitempty"` + IngressWorkerFilter string `json:"ingress_worker_filter,omitempty"` + Attributes *SshTargetAttributes `json:"attributes,omitempty"` +} diff --git a/api/sessionrecordings/user.gen.go b/api/sessionrecordings/user.gen.go new file mode 100644 index 0000000000..0790d30a87 --- /dev/null +++ b/api/sessionrecordings/user.gen.go @@ -0,0 +1,13 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +import ( + "github.com/hashicorp/boundary/api/scopes" +) + +type User struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Scope *scopes.ScopeInfo `json:"scope,omitempty"` +} diff --git a/api/sessionrecordings/values_at_time.gen.go b/api/sessionrecordings/values_at_time.gen.go new file mode 100644 index 0000000000..c7070f230e --- /dev/null +++ b/api/sessionrecordings/values_at_time.gen.go @@ -0,0 +1,7 @@ +// Code generated by "make api"; DO NOT EDIT. +package sessionrecordings + +type ValuesAtTime struct { + User *User `json:"user,omitempty"` + Target *Target `json:"target,omitempty"` +} diff --git a/internal/api/genapi/input.go b/internal/api/genapi/input.go index 95ead80b3f..f9f0ad815a 100644 --- a/internal/api/genapi/input.go +++ b/internal/api/genapi/input.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/plugins" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/roles" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/session_recordings" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/sessions" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets" @@ -1007,6 +1008,43 @@ var inputStructs = []*structInfo{ versionEnabled: true, recursiveListing: true, }, + { + inProto: &session_recordings.SessionRecording{}, + outFile: "sessionrecordings/session_recording.gen.go", + templates: []*template.Template{ + clientTemplate, + readTemplate, + listTemplate, + }, + pluralResourceName: "session-recordings", + createResponseTypes: []string{ReadResponseType, ListResponseType}, + recursiveListing: true, + versionEnabled: false, + }, + { + inProto: &session_recordings.User{}, + outFile: "sessionrecordings/user.gen.go", + }, + { + inProto: &session_recordings.Target{}, + outFile: "sessionrecordings/target.gen.go", + }, + { + inProto: &session_recordings.SshTargetAttributes{}, + outFile: "sessionrecordings/ssh_target_attributes.gen.go", + }, + { + inProto: &session_recordings.ValuesAtTime{}, + outFile: "sessionrecordings/values_at_time.gen.go", + }, + { + inProto: &session_recordings.ConnectionRecording{}, + outFile: "sessionrecordings/connection_recording.gen.go", + }, + { + inProto: &session_recordings.ChannelRecording{}, + outFile: "sessionrecordings/channel_recording.gen.go", + }, { inProto: &workers.Certificate{}, outFile: "workers/certificate.gen.go", diff --git a/internal/api/genapi/parse.go b/internal/api/genapi/parse.go index da38e4642d..a654343b61 100644 --- a/internal/api/genapi/parse.go +++ b/internal/api/genapi/parse.go @@ -7,16 +7,15 @@ import ( "fmt" _struct "github.com/golang/protobuf/ptypes/struct" + "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/iancoleman/strcase" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" - - "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" - "github.com/hashicorp/go-secure-stdlib/strutil" - - "github.com/iancoleman/strcase" ) func printDebug(desc protoreflect.MessageDescriptor) { @@ -106,6 +105,7 @@ var ( int32ValueName = (&wrapperspb.Int32Value{}).ProtoReflect().Descriptor().FullName() structValueName = (&_struct.Struct{}).ProtoReflect().Descriptor().FullName() timestampName = (×tamppb.Timestamp{}).ProtoReflect().Descriptor().FullName() + durationName = (&durationpb.Duration{}).ProtoReflect().Descriptor().FullName() valueName = (&_struct.Value{}).ProtoReflect().Descriptor().FullName() ) @@ -125,6 +125,8 @@ func messageKind(fd protoreflect.FieldDescriptor) (ptr, pkg, name string) { return "", "", "interface{}" case timestampName: return "", "time", "Time" + case durationName: + return "", "time", "Duration" default: return "*", packageFromFullName(fd.Message().FullName()), string(fd.Message().Name()) }