Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #287 from CodeNoobKing/feat/ark_deploy
Browse files Browse the repository at this point in the history
Feat/ark deploy
  • Loading branch information
lvjing2 authored Nov 14, 2023
2 parents 0fa5b3b + dcebe0f commit 1f87665
Show file tree
Hide file tree
Showing 26 changed files with 2,142 additions and 345 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/arkctl_unit_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Set up go
uses: actions/setup-go@v4
with:
go-version: '1.20.5'
go-version: '1.21.1'
cache-dependency-path: ${{ env.WORK_DIR }}/go.sum

- name: Run go mod
Expand Down
177 changes: 177 additions & 0 deletions arkctl/common/cmdutil/cmd_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cmdutil

import (
"bufio"
"context"
"errors"
"os/exec"
"strings"
"sync/atomic"
)

// Command is a wrapper for executing cmd command
type Command interface {
// Exec execute the command
Exec() error

// GetCommand return the command
GetCommand() string

// GetArgs return the args
GetArgs() []string

// Output return the output of command
Output() <-chan string

// Wait wait for command to finish
// is stderr is not empty, send an error
Wait() <-chan error

// GetExitState return the exit state of command
GetExitError() error

// Kill the command
Kill() error

// String return the string representation of command
String() string
}

// BuildCommand return a new Command
func BuildCommand(
ctx context.Context,
cmd string, args ...string) Command {
return BuildCommandWithWorkDir(ctx, "", cmd, args...)
}

func BuildCommandWithWorkDir(
ctx context.Context,
workdir string,
cmd string,
args ...string) Command {
cancableContext, cancelFunc := context.WithCancel(ctx)
return &command{
ctx: cancableContext,
cmd: cmd,
workdir: workdir,
args: args,
output: make(chan string, 1),
completeSignal: make(chan error, 1),
cancel: cancelFunc,
}
}

type command struct {
started *atomic.Bool
ctx context.Context
workdir string
cmd string
args []string

cancel context.CancelFunc
output chan string
completeSignal chan error
exitState error
}

func (c *command) String() string {
return c.cmd + " " + strings.Join(c.args, " ")
}

func (c *command) GetCommand() string {
return c.cmd
}

func (c *command) GetArgs() []string {
return c.args
}

func (c *command) GetExitError() error {
return c.exitState
}

func (c *command) Exec() error {
execCmd := exec.CommandContext(c.ctx, c.cmd, c.args...)
execCmd.Dir = c.workdir

stdoutpipeline, err := execCmd.StdoutPipe()

if err != nil {
return err
}

stderrorPipeline, err := execCmd.StderrPipe()
if err != nil {
return err
}

closed := &atomic.Bool{}
closed.Store(false)
closeCompleteSignal := func(err error) {
if closed.CompareAndSwap(false, true) {
if err != nil {
c.completeSignal <- err
}
close(c.completeSignal)
}
}

go func() {
scanner := bufio.NewScanner(stdoutpipeline)
for scanner.Scan() {
c.output <- scanner.Text()
}
close(c.output)
}()

go func() {
scanner := bufio.NewScanner(stderrorPipeline)
sb := &strings.Builder{}
for scanner.Scan() {
sb.WriteString(scanner.Text())
sb.WriteString("\n")
}

if len(sb.String()) != 0 {
closeCompleteSignal(errors.New(sb.String()))
}
}()

if err := execCmd.Start(); err != nil {
return err
}

go func() {
c.exitState = execCmd.Wait()
closeCompleteSignal(nil)
}()

return nil
}

func (c *command) Wait() <-chan error {
return c.completeSignal
}

func (c *command) Kill() error {
c.cancel()
return nil
}

func (c *command) Output() <-chan string {
return c.output
}
79 changes: 79 additions & 0 deletions arkctl/common/cmdutil/cmd_util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cmdutil

import (
"context"
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestCommand_HappyPath(t *testing.T) {
// ls command is available in most platform
cmd := BuildCommand(context.Background(), "ls", "-l")

err := cmd.Exec()
assert.Nil(t, err)

output := cmd.Output()
containsCurFile := false
for line := range output {
fmt.Println(line)
containsCurFile = containsCurFile || strings.Contains(line, "cmd_util_test.go")
}
assert.True(t, containsCurFile)

for err := range cmd.Wait() {
assert.Nil(t, err)
}

err = cmd.Kill()
assert.Nil(t, err)
}

func TestCommand_StdErr(t *testing.T) {
cmd := BuildCommand(context.Background(), "ls", "-l", "/not/exist/path")

err := cmd.Exec()
assert.Nil(t, err)

output := cmd.Output()
hasStdout := false
for _ = range output {
hasStdout = true
}
assert.False(t, hasStdout)

for err := range cmd.Wait() {
assert.NotNil(t, err)
assert.True(t, len(err.Error()) != 0)
}

err = cmd.Kill()
assert.Nil(t, err)

err = cmd.GetExitError()
assert.Nil(t, err)
}

func TestCommand_WrongCommannd(t *testing.T) {
cmd := BuildCommand(context.Background(), "not_exist_command")
err := cmd.Exec()
assert.NotNil(t, err)
assert.True(t, len(err.Error()) != 0)
}
15 changes: 15 additions & 0 deletions arkctl/common/cmdutil/pterm_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cmdutil
52 changes: 52 additions & 0 deletions arkctl/common/contextutil/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package contextutil

import "golang.org/x/net/context"

type Context struct {
context.Context
value map[interface{}]interface{}
}

// NewContext return a new Context
func NewContext(ctx context.Context) *Context {
return &Context{
Context: ctx,
value: make(map[interface{}]interface{}),
}
}

// Put add kv to current context
func (ctx *Context) Put(key, value interface{}) context.Context {
if ctx.value == nil {
ctx.value = make(map[interface{}]interface{})
}
ctx.value[key] = value
return ctx
}

// Value get value from context
func (ctx *Context) Value(key interface{}) interface{} {
if ctx.value == nil {
return ctx.Context.Value(key)
}

value, ok := ctx.value[key]
if !ok {
return ctx.Context.Value(key)
}
return value
}
36 changes: 36 additions & 0 deletions arkctl/common/contextutil/context_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package contextutil

import (
"context"
"github.com/sirupsen/logrus"
)

var (
enableLogger = true
)

func DisableLogger() {
enableLogger = false
}

func GetLogger(ctx context.Context) logrus.FieldLogger {
logger := logrus.WithContext(ctx)
if !enableLogger {
logger.Logger.Level = logrus.FatalLevel
}
return logger
}
Loading

0 comments on commit 1f87665

Please sign in to comment.