From 738a6c64726d99eba7d755c5e043f85b43756898 Mon Sep 17 00:00:00 2001 From: Timothy Messier Date: Thu, 11 May 2023 20:41:18 +0000 Subject: [PATCH] feat(bsr/convert): Implement ToAsciicast This adds support for converting a channel in a SSH BSR to an asciicast. --- internal/bsr/convert/convert.go | 58 +++++++++++++++++++++++++++++++-- internal/bsr/convert/doc.go | 5 +++ 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 internal/bsr/convert/doc.go diff --git a/internal/bsr/convert/convert.go b/internal/bsr/convert/convert.go index da24d337a..2b2d7ec7b 100644 --- a/internal/bsr/convert/convert.go +++ b/internal/bsr/convert/convert.go @@ -5,16 +5,68 @@ package convert import ( "context" + "fmt" "io" "github.com/hashicorp/boundary/internal/bsr" + "github.com/hashicorp/boundary/internal/bsr/internal/is" + "github.com/hashicorp/boundary/internal/bsr/ssh" "github.com/hashicorp/boundary/internal/storage" ) // ToAsciicast accepts a bsr.Session and will convert the underlying BSR connection or channel file to an asciinema file. // The tempFs will be used to write the asciinema file to disk // It returns an io.Reader to the converted asciinema file -// Supports WithChannelId() to indicate this conversion should occur on a chanel on a multiplexed session -func ToAsciicast(ctx context.Context, session bsr.Session, tmp storage.TempFile, connectionId string, options ...Option) (io.ReadCloser, error) { - panic("not implemented") +// Supports WithChannelId() to indicate this conversion should occur on a channel on a multiplexed session +func ToAsciicast(ctx context.Context, session *bsr.Session, tmp storage.TempFile, connectionId string, options ...Option) (io.ReadCloser, error) { + const op = "convert.ToAsciicast" + + switch { + case is.Nil(session): + return nil, fmt.Errorf("%s: missing session: %w", op, bsr.ErrInvalidParameter) + case is.Nil(session.Meta): + return nil, fmt.Errorf("%s: missing session meta: %w", op, bsr.ErrInvalidParameter) + case is.Nil(tmp): + return nil, fmt.Errorf("%s: missing temp file: %w", op, bsr.ErrInvalidParameter) + case connectionId == "": + return nil, fmt.Errorf("%s: missing connection id: %w", op, bsr.ErrInvalidParameter) + } + + opts := getOpts(options...) + + switch session.Meta.Protocol { + case ssh.Protocol: + chanId := opts.withChannelId + switch { + case chanId == "": + return nil, fmt.Errorf("%s: protocol %q requires channel id to convert: %w", op, ssh.Protocol, bsr.ErrInvalidParameter) + } + + conn, err := session.OpenConnection(ctx, connectionId) + if err != nil { + return nil, fmt.Errorf("%s: %w", op, err) + } + + ch, err := conn.OpenChannel(ctx, chanId) + if err != nil { + return nil, fmt.Errorf("%s: %w", op, err) + } + + // TODO sanity checks before getting the data files: + // - check connection summary to see if there was an exec or shell request + + reqScanner, err := ch.OpenRequestScanner(ctx, bsr.Inbound) + if err != nil { + return nil, fmt.Errorf("%s: %w", op, err) + } + + msgScanner, err := ch.OpenMessageScanner(ctx, bsr.Outbound) + if err != nil { + return nil, fmt.Errorf("%s: %w", op, err) + } + + return sshChannelToAsciicast(ctx, reqScanner, msgScanner, tmp) + default: + return nil, fmt.Errorf("%s: %w", op, ErrUnsupportedProtocol) + } } diff --git a/internal/bsr/convert/doc.go b/internal/bsr/convert/doc.go new file mode 100644 index 000000000..45dcb4a50 --- /dev/null +++ b/internal/bsr/convert/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package convert provides functions for converting BSR data into other formats. +package convert