Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire up delete #58

Merged
merged 2 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions provider/pkg/provider/cmd/tee.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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()
}
2 changes: 1 addition & 1 deletion provider/pkg/provider/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions provider/pkg/provider/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
36 changes: 36 additions & 0 deletions provider/pkg/provisioner/cmd/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ 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:
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 {
Expand Down Expand Up @@ -49,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:
Expand Down
72 changes: 57 additions & 15 deletions tests/tee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
)

var _ = Describe("Tee", Ordered, func() {
urn := util.Urn("Tee", "cmd")
var server integration.Server

BeforeAll(func(ctx context.Context) {
Expand All @@ -22,44 +23,85 @@ 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{
"files": resource.NewArrayProperty([]resource.PropertyValue{
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(MatchError(ContainSubstring("Could not find the file /tmp/tee/create.txt")))
})
})
})