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

Commit 1f87665

Browse files
authored
Merge pull request #287 from CodeNoobKing/feat/ark_deploy
Feat/ark deploy
2 parents 0fa5b3b + dcebe0f commit 1f87665

File tree

26 files changed

+2142
-345
lines changed

26 files changed

+2142
-345
lines changed

.github/workflows/arkctl_unit_test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
- name: Set up go
3636
uses: actions/setup-go@v4
3737
with:
38-
go-version: '1.20.5'
38+
go-version: '1.21.1'
3939
cache-dependency-path: ${{ env.WORK_DIR }}/go.sum
4040

4141
- name: Run go mod

arkctl/common/cmdutil/cmd_util.go

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package cmdutil
16+
17+
import (
18+
"bufio"
19+
"context"
20+
"errors"
21+
"os/exec"
22+
"strings"
23+
"sync/atomic"
24+
)
25+
26+
// Command is a wrapper for executing cmd command
27+
type Command interface {
28+
// Exec execute the command
29+
Exec() error
30+
31+
// GetCommand return the command
32+
GetCommand() string
33+
34+
// GetArgs return the args
35+
GetArgs() []string
36+
37+
// Output return the output of command
38+
Output() <-chan string
39+
40+
// Wait wait for command to finish
41+
// is stderr is not empty, send an error
42+
Wait() <-chan error
43+
44+
// GetExitState return the exit state of command
45+
GetExitError() error
46+
47+
// Kill the command
48+
Kill() error
49+
50+
// String return the string representation of command
51+
String() string
52+
}
53+
54+
// BuildCommand return a new Command
55+
func BuildCommand(
56+
ctx context.Context,
57+
cmd string, args ...string) Command {
58+
return BuildCommandWithWorkDir(ctx, "", cmd, args...)
59+
}
60+
61+
func BuildCommandWithWorkDir(
62+
ctx context.Context,
63+
workdir string,
64+
cmd string,
65+
args ...string) Command {
66+
cancableContext, cancelFunc := context.WithCancel(ctx)
67+
return &command{
68+
ctx: cancableContext,
69+
cmd: cmd,
70+
workdir: workdir,
71+
args: args,
72+
output: make(chan string, 1),
73+
completeSignal: make(chan error, 1),
74+
cancel: cancelFunc,
75+
}
76+
}
77+
78+
type command struct {
79+
started *atomic.Bool
80+
ctx context.Context
81+
workdir string
82+
cmd string
83+
args []string
84+
85+
cancel context.CancelFunc
86+
output chan string
87+
completeSignal chan error
88+
exitState error
89+
}
90+
91+
func (c *command) String() string {
92+
return c.cmd + " " + strings.Join(c.args, " ")
93+
}
94+
95+
func (c *command) GetCommand() string {
96+
return c.cmd
97+
}
98+
99+
func (c *command) GetArgs() []string {
100+
return c.args
101+
}
102+
103+
func (c *command) GetExitError() error {
104+
return c.exitState
105+
}
106+
107+
func (c *command) Exec() error {
108+
execCmd := exec.CommandContext(c.ctx, c.cmd, c.args...)
109+
execCmd.Dir = c.workdir
110+
111+
stdoutpipeline, err := execCmd.StdoutPipe()
112+
113+
if err != nil {
114+
return err
115+
}
116+
117+
stderrorPipeline, err := execCmd.StderrPipe()
118+
if err != nil {
119+
return err
120+
}
121+
122+
closed := &atomic.Bool{}
123+
closed.Store(false)
124+
closeCompleteSignal := func(err error) {
125+
if closed.CompareAndSwap(false, true) {
126+
if err != nil {
127+
c.completeSignal <- err
128+
}
129+
close(c.completeSignal)
130+
}
131+
}
132+
133+
go func() {
134+
scanner := bufio.NewScanner(stdoutpipeline)
135+
for scanner.Scan() {
136+
c.output <- scanner.Text()
137+
}
138+
close(c.output)
139+
}()
140+
141+
go func() {
142+
scanner := bufio.NewScanner(stderrorPipeline)
143+
sb := &strings.Builder{}
144+
for scanner.Scan() {
145+
sb.WriteString(scanner.Text())
146+
sb.WriteString("\n")
147+
}
148+
149+
if len(sb.String()) != 0 {
150+
closeCompleteSignal(errors.New(sb.String()))
151+
}
152+
}()
153+
154+
if err := execCmd.Start(); err != nil {
155+
return err
156+
}
157+
158+
go func() {
159+
c.exitState = execCmd.Wait()
160+
closeCompleteSignal(nil)
161+
}()
162+
163+
return nil
164+
}
165+
166+
func (c *command) Wait() <-chan error {
167+
return c.completeSignal
168+
}
169+
170+
func (c *command) Kill() error {
171+
c.cancel()
172+
return nil
173+
}
174+
175+
func (c *command) Output() <-chan string {
176+
return c.output
177+
}
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package cmdutil
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"strings"
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestCommand_HappyPath(t *testing.T) {
27+
// ls command is available in most platform
28+
cmd := BuildCommand(context.Background(), "ls", "-l")
29+
30+
err := cmd.Exec()
31+
assert.Nil(t, err)
32+
33+
output := cmd.Output()
34+
containsCurFile := false
35+
for line := range output {
36+
fmt.Println(line)
37+
containsCurFile = containsCurFile || strings.Contains(line, "cmd_util_test.go")
38+
}
39+
assert.True(t, containsCurFile)
40+
41+
for err := range cmd.Wait() {
42+
assert.Nil(t, err)
43+
}
44+
45+
err = cmd.Kill()
46+
assert.Nil(t, err)
47+
}
48+
49+
func TestCommand_StdErr(t *testing.T) {
50+
cmd := BuildCommand(context.Background(), "ls", "-l", "/not/exist/path")
51+
52+
err := cmd.Exec()
53+
assert.Nil(t, err)
54+
55+
output := cmd.Output()
56+
hasStdout := false
57+
for _ = range output {
58+
hasStdout = true
59+
}
60+
assert.False(t, hasStdout)
61+
62+
for err := range cmd.Wait() {
63+
assert.NotNil(t, err)
64+
assert.True(t, len(err.Error()) != 0)
65+
}
66+
67+
err = cmd.Kill()
68+
assert.Nil(t, err)
69+
70+
err = cmd.GetExitError()
71+
assert.Nil(t, err)
72+
}
73+
74+
func TestCommand_WrongCommannd(t *testing.T) {
75+
cmd := BuildCommand(context.Background(), "not_exist_command")
76+
err := cmd.Exec()
77+
assert.NotNil(t, err)
78+
assert.True(t, len(err.Error()) != 0)
79+
}

arkctl/common/cmdutil/pterm_util.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package cmdutil

arkctl/common/contextutil/context.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package contextutil
16+
17+
import "golang.org/x/net/context"
18+
19+
type Context struct {
20+
context.Context
21+
value map[interface{}]interface{}
22+
}
23+
24+
// NewContext return a new Context
25+
func NewContext(ctx context.Context) *Context {
26+
return &Context{
27+
Context: ctx,
28+
value: make(map[interface{}]interface{}),
29+
}
30+
}
31+
32+
// Put add kv to current context
33+
func (ctx *Context) Put(key, value interface{}) context.Context {
34+
if ctx.value == nil {
35+
ctx.value = make(map[interface{}]interface{})
36+
}
37+
ctx.value[key] = value
38+
return ctx
39+
}
40+
41+
// Value get value from context
42+
func (ctx *Context) Value(key interface{}) interface{} {
43+
if ctx.value == nil {
44+
return ctx.Context.Value(key)
45+
}
46+
47+
value, ok := ctx.value[key]
48+
if !ok {
49+
return ctx.Context.Value(key)
50+
}
51+
return value
52+
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package contextutil
16+
17+
import (
18+
"context"
19+
"github.com/sirupsen/logrus"
20+
)
21+
22+
var (
23+
enableLogger = true
24+
)
25+
26+
func DisableLogger() {
27+
enableLogger = false
28+
}
29+
30+
func GetLogger(ctx context.Context) logrus.FieldLogger {
31+
logger := logrus.WithContext(ctx)
32+
if !enableLogger {
33+
logger.Logger.Level = logrus.FatalLevel
34+
}
35+
return logger
36+
}

0 commit comments

Comments
 (0)