From c5d16ac0221b536664c08cef420697ff43fbd38a Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Mon, 29 Jul 2024 04:45:56 -0500 Subject: [PATCH 1/2] Git it kinda close to where it needs to be --- provider/pkg/provider/cmd/tee.go | 52 +++++++++++++++--- provider/pkg/provider/config.go | 2 +- provider/pkg/provider/provisioner.go | 16 ++++-- provider/pkg/provisioner/cmd/service.go | 6 +++ tests/tee_test.go | 72 +++++++++++++++++++------ 5 files changed, 122 insertions(+), 26 deletions(-) diff --git a/provider/pkg/provider/cmd/tee.go b/provider/pkg/provider/cmd/tee.go index 6eca34d1..afbdfd09 100644 --- a/provider/pkg/provider/cmd/tee.go +++ b/provider/pkg/provider/cmd/tee.go @@ -33,27 +33,36 @@ type TeeArgs struct { type TeeState struct { TeeArgs - Stderr string `pulumi:"stderr"` - Stdout string `pulumi:"stdout"` + CreatedFiles []string `pulumi:"created_files"` + Stderr string `pulumi:"stderr"` + Stdout string `pulumi:"stdout"` } -func (Tee) Create(ctx context.Context, name string, input TeeArgs, preview bool) (string, TeeState, error) { +var _ infer.CustomCreate[TeeArgs, TeeState] = Tee{} +var _ infer.CustomDelete[TeeState] = Tee{} + +// Create implements infer.CustomCreate. +func (Tee) Create(ctx context.Context, name string, inputs TeeArgs, preview bool) (string, TeeState, error) { state := TeeState{} - if err := state.create(ctx, input); err != nil { + if err := state.create(ctx, inputs); err != nil { return name, state, err } return name, state, nil } +// Delete implements infer.CustomDelete. +func (Tee) Delete(ctx context.Context, id string, props TeeState) error { + return props.delete(ctx) +} + func (state *TeeState) create(ctx context.Context, input TeeArgs) error { - c := infer.GetConfig[provider.Config](ctx) - p, err := c.NewProvisioner() + p, err := provisioner(ctx) if err != nil { return err } - res, err := p.Cmd.Command(ctx, &pb.CommandRequest{ + res, err := p.Cmd().Command(ctx, &pb.CommandRequest{ Op: pb.Op_OP_CREATE, Command: pb.Command_COMMAND_TEE, Args: input.Create.Files, @@ -64,8 +73,37 @@ func (state *TeeState) create(ctx context.Context, input TeeArgs) error { return err } + state.CreatedFiles = input.Create.Files + state.Stderr = res.Stderr + state.Stdout = res.Stdout + + return nil +} + +func (state *TeeState) delete(ctx context.Context) error { + p, err := provisioner(ctx) + if err != nil { + return err + } + + res, err := p.Cmd().Command(ctx, &pb.CommandRequest{ + Op: pb.Op_OP_DELETE, + Command: pb.Command_COMMAND_TEE, + Args: state.CreatedFiles, + Flags: map[string]*pb.Flag{}, + }) + if err != nil { + return err + } + + state.CreatedFiles = []string{} state.Stderr = res.Stderr state.Stdout = res.Stdout return nil } + +func provisioner(ctx context.Context) (provider.Provisioner, error) { + c := infer.GetConfig[provider.Config](ctx) + return c.NewProvisioner() +} diff --git a/provider/pkg/provider/config.go b/provider/pkg/provider/config.go index 4fefb8ad..e7afaead 100644 --- a/provider/pkg/provider/config.go +++ b/provider/pkg/provider/config.go @@ -19,7 +19,7 @@ func (c Config) NewGrpcClient() (*grpc.ClientConn, error) { ) } -func (c Config) NewProvisioner() (*provisioner, error) { +func (c Config) NewProvisioner() (Provisioner, error) { conn, err := c.NewGrpcClient() if err != nil { return nil, err diff --git a/provider/pkg/provider/provisioner.go b/provider/pkg/provider/provisioner.go index 6f773e82..d17bcfee 100644 --- a/provider/pkg/provider/provisioner.go +++ b/provider/pkg/provider/provisioner.go @@ -7,17 +7,27 @@ import ( "google.golang.org/grpc" ) +type Provisioner interface { + io.Closer + Cmd() pb.CommandServiceClient +} + type provisioner struct { - Cmd pb.CommandServiceClient + cmd pb.CommandServiceClient conn *grpc.ClientConn } -var _ io.Closer = &provisioner{} +var _ Provisioner = &provisioner{} func NewProvisioner(conn *grpc.ClientConn) *provisioner { cmd := pb.NewCommandServiceClient(conn) - return &provisioner{Cmd: cmd, conn: conn} + return &provisioner{cmd: cmd, conn: conn} +} + +// Cmd implements Provisioner +func (p *provisioner) Cmd() pb.CommandServiceClient { + return p.cmd } // Close implements io.Closer. diff --git a/provider/pkg/provisioner/cmd/service.go b/provider/pkg/provisioner/cmd/service.go index 4725dd0d..df39d834 100644 --- a/provider/pkg/provisioner/cmd/service.go +++ b/provider/pkg/provisioner/cmd/service.go @@ -22,6 +22,12 @@ func NewServer() pb.CommandServiceServer { // Command implements baremetalv1alpha1.CommandServiceServer. func (c *service) Command(ctx context.Context, req *pb.CommandRequest) (*pb.CommandResponse, error) { + switch req.Op { + case pb.Op_OP_CREATE: + default: + return nil, fmt.Errorf("unsupported op: %s", req.Op) + } + log := c.log.With("op", req.Op) bin, err := getBin(req.Command) if err != nil { diff --git a/tests/tee_test.go b/tests/tee_test.go index 4e457a4e..57737b51 100644 --- a/tests/tee_test.go +++ b/tests/tee_test.go @@ -14,6 +14,7 @@ import ( ) var _ = Describe("Tee", Ordered, func() { + urn := util.Urn("Tee", "cmd") var server integration.Server BeforeAll(func(ctx context.Context) { @@ -22,24 +23,65 @@ var _ = Describe("Tee", Ordered, func() { Expect(err).NotTo(HaveOccurred()) }) - BeforeEach(func(ctx context.Context) { + BeforeAll(func(ctx context.Context) { By("creating a provider server") server = util.NewIntegrationProvider() + }) + BeforeAll(func(ctx context.Context) { By("configuring the provider") err := provisioner.ConfigureProvider(ctx, server) Expect(err).NotTo(HaveOccurred()) }) - Describe("Create", func() { - It("should write stdin to a file", func(ctx context.Context) { - stdin := "Test stdin" - file := "/tmp/tee/create.txt" + Describe("write stdin to a file", func() { + stdin := "Test stdin" + file := "/tmp/tee/create.txt" + + var teeId *string + var stdout *string + var stderr *string - By("creating the resource") + props := resource.PropertyMap{ + "stdin": resource.NewStringProperty(stdin), + "create": resource.NewObjectProperty(resource.PropertyMap{ + "files": resource.NewArrayProperty([]resource.PropertyValue{ + resource.NewStringProperty(file), + }), + }), + } + + It("should create", func(ctx context.Context) { response, err := server.Create(p.CreateRequest{ - Urn: util.Urn("Tee", "cmd"), - Preview: false, + Urn: urn, + Preview: false, + Properties: props, + }) + + Expect(err).NotTo(HaveOccurred()) + teeId = &response.ID + + e, ok := response.Properties["stderr"].V.(string) + Expect(ok).To(BeTrueBecause("stderr was not a string")) + stderr = &e + + o, ok := response.Properties["stdout"].V.(string) + Expect(ok).To(BeTrueBecause("stdout was not a string")) + stdout = &o + + By("attempting to copy the created file") + reader, err := provisioner.Ctr().CopyFileFromContainer(ctx, file) + Expect(err).NotTo(HaveOccurred()) + result, err := io.ReadAll(reader) + Expect(err).NotTo(HaveOccurred()) + Expect(string(result)).To(Equal(stdin)) + }) + + It("should delete", func(ctx context.Context) { + Expect(teeId).NotTo(BeNil()) + err := server.Delete(p.DeleteRequest{ + Urn: urn, + ID: *teeId, Properties: resource.PropertyMap{ "stdin": resource.NewStringProperty(stdin), "create": resource.NewObjectProperty(resource.PropertyMap{ @@ -47,19 +89,19 @@ var _ = Describe("Tee", Ordered, func() { resource.NewStringProperty(file), }), }), + "stdout": resource.NewStringProperty(*stdout), + "stderr": resource.NewStringProperty(*stderr), + "created_files": resource.NewArrayProperty([]resource.PropertyValue{ + resource.NewStringProperty(file), + }), }, }) Expect(err).NotTo(HaveOccurred()) - Expect(response.Properties["stderr"].V).To(BeEmpty()) - Expect(response.Properties["stdout"].V).To(Equal(stdin)) By("attempting to copy the created file") - reader, err := provisioner.Ctr().CopyFileFromContainer(ctx, file) - Expect(err).NotTo(HaveOccurred()) - result, err := io.ReadAll(reader) - Expect(err).NotTo(HaveOccurred()) - Expect(string(result)).To(Equal(stdin)) + _, err = provisioner.Ctr().CopyFileFromContainer(ctx, file) + Expect(err).To(HaveOccurred()) // Can we be more specific }) }) }) From ba1f15b84016da4521847f54601f9f6e70aa9f21 Mon Sep 17 00:00:00 2001 From: Erik Rasmussen Date: Mon, 29 Jul 2024 04:59:48 -0500 Subject: [PATCH 2/2] Set up delete tests --- provider/pkg/provisioner/cmd/service.go | 34 +++++++++++++++++++++++-- tests/tee_test.go | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/provider/pkg/provisioner/cmd/service.go b/provider/pkg/provisioner/cmd/service.go index df39d834..ad530fbd 100644 --- a/provider/pkg/provisioner/cmd/service.go +++ b/provider/pkg/provisioner/cmd/service.go @@ -24,10 +24,15 @@ func NewServer() pb.CommandServiceServer { func (c *service) Command(ctx context.Context, req *pb.CommandRequest) (*pb.CommandResponse, error) { switch req.Op { case pb.Op_OP_CREATE: - default: - return nil, fmt.Errorf("unsupported op: %s", req.Op) + return c.create(ctx, req) + case pb.Op_OP_DELETE: + return c.delete(ctx, req) } + return nil, fmt.Errorf("unsupported op: %s", req.Op) +} + +func (c *service) create(ctx context.Context, req *pb.CommandRequest) (*pb.CommandResponse, error) { log := c.log.With("op", req.Op) bin, err := getBin(req.Command) if err != nil { @@ -55,6 +60,31 @@ func (c *service) Command(ctx context.Context, req *pb.CommandRequest) (*pb.Comm }, nil } +func (c *service) delete(ctx context.Context, req *pb.CommandRequest) (*pb.CommandResponse, error) { + log := c.log.With("op", req.Op) + bin := "rm" + + cmd := exec.CommandContext(ctx, bin, req.Args...) + log = log.With("bin", bin, "args", req.Args) + + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + cmd.Stdin = strings.NewReader(req.Stdin) + cmd.Stdout = stdout + cmd.Stderr = stderr + + log.Info("executing command") + err := cmd.Run() + if err != nil { + log.Error("command failed", "err", err) + } + + return &pb.CommandResponse{ + Stdout: stdout.String(), + Stderr: stderr.String(), + }, nil +} + func getBin(cmd pb.Command) (string, error) { switch cmd { case pb.Command_COMMAND_TEE: diff --git a/tests/tee_test.go b/tests/tee_test.go index 57737b51..6558d324 100644 --- a/tests/tee_test.go +++ b/tests/tee_test.go @@ -101,7 +101,7 @@ var _ = Describe("Tee", Ordered, func() { By("attempting to copy the created file") _, err = provisioner.Ctr().CopyFileFromContainer(ctx, file) - Expect(err).To(HaveOccurred()) // Can we be more specific + Expect(err).To(MatchError(ContainSubstring("Could not find the file /tmp/tee/create.txt"))) }) }) })