Skip to content

Commit

Permalink
init project
Browse files Browse the repository at this point in the history
  • Loading branch information
xcaspar committed Nov 3, 2019
0 parents commit d6a4121
Show file tree
Hide file tree
Showing 18 changed files with 1,684 additions and 0 deletions.
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test
.vagrant
releases
tmp
.idea/

# Architecture specific extensions/prefixes
trace.out
*.out
.DS_Store

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof
profile.cov
coverage.html

# Emacs backup files
*~

# ctags files
tags

# Project specific
/chaosblade
/blade
/bin/java-agent*.jar
/hack/chaosblade*
/hack/lib/**
/chaosblade.dat
target
coverage.txt
vendor

# Website
site-build
194 changes: 194 additions & 0 deletions channel/channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* 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 channel

import (
"context"
"fmt"
"os/exec"
"strings"
"time"
"os"
"strconv"

"github.com/sirupsen/logrus"

"github.com/chaosblade-io/chaosblade-spec-go/spec"
"github.com/chaosblade-io/chaosblade-spec-go/util"
)

// Run the script in the local with ctx and returns spec.Response
func Run(ctx context.Context, script, args string) *spec.Response {
return execScript(ctx, script, args)
}

// GetScriptPath returns the script path for running
func GetScriptPath() string {
return util.GetBinPath()
}

// execScript invokes exec.CommandContext
func execScript(ctx context.Context, script, args string) *spec.Response {
newCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
if ctx == context.Background() {
ctx = newCtx
}
script = strings.Replace(script, " ", `\ `, -1)
logrus.Debugf("script: %s %s", script, args)
cmd := exec.CommandContext(ctx, "/bin/sh", "-c", script+" "+args)
output, err := cmd.CombinedOutput()
if err != nil {
errMsg := fmt.Sprintf(string(output) + " " + err.Error())
return spec.ReturnFail(spec.Code[spec.ExecCommandError], errMsg)
}
result := string(output)
return spec.ReturnSuccess(result)
}

// GetPidsByProcessCmdName returns the matched process other than the current process
func GetPidsByProcessCmdName(processName string, ctx context.Context) ([]string, error) {
excludeProcess := ctx.Value(ExcludeProcessKey)
excludeGrepInfo := ""
if excludeProcess != nil {
excludeProcessString := excludeProcess.(string)
if excludeProcessString != "" {
excludeGrepInfo = fmt.Sprintf(`| grep -v -w %s`, excludeProcessString)
}
}
response := Run(ctx, "pgrep",
fmt.Sprintf(`-l %s %s | grep -v -w chaos_killprocess | grep -v -w chaos_stopprocess | awk '{print $1}' | tr '\n' ' '`,
processName, excludeGrepInfo))
if !response.Success {
return nil, fmt.Errorf(response.Err)
}
pidString := response.Result.(string)
pids := strings.Fields(strings.TrimSpace(pidString))
currPid := strconv.Itoa(os.Getpid())
for idx, pid := range pids {
if pid == currPid {
return util.Remove(pids, idx), nil
}
}
return pids, nil
}

// grep ${key}
const ProcessKey = "process"
const ExcludeProcessKey = "excludeProcess"

// GetPidsByProcessName returns the matched process other than the current process
func GetPidsByProcessName(processName string, ctx context.Context) ([]string, error) {
psArgs := GetPsArgs()
otherProcess := ctx.Value(ProcessKey)
otherGrepInfo := ""
if otherProcess != nil {
processString := otherProcess.(string)
if processString != "" {
otherGrepInfo = fmt.Sprintf(`| grep "%s"`, processString)
}
}
excludeProcess := ctx.Value(ExcludeProcessKey)
excludeGrepInfo := ""
if excludeProcess != nil {
excludeProcessString := excludeProcess.(string)
if excludeProcessString != "" {
excludeGrepInfo = fmt.Sprintf(`| grep -v -w %s`, excludeProcessString)
}
}
response := Run(ctx, "ps",
fmt.Sprintf(`%s | grep "%s" %s %s | grep -v -w grep | grep -v -w chaos_killprocess | grep -v -w chaos_stopprocess | awk '{print $2}' | tr '\n' ' '`,
psArgs, processName, otherGrepInfo, excludeGrepInfo))
if !response.Success {
return nil, fmt.Errorf(response.Err)
}
pidString := strings.TrimSpace(response.Result.(string))
if pidString == "" {
return make([]string, 0), nil
}
pids := strings.Fields(pidString)
currPid := strconv.Itoa(os.Getpid())
for idx, pid := range pids {
if pid == currPid {
return util.Remove(pids, idx), nil
}
}
return pids, nil
}

// GetPsArgs for querying the process info
func GetPsArgs() string {
var psArgs = "-eo user,pid,ppid,args"
if isAlpinePlatform() {
psArgs = "-o user,pid,ppid,args"
}
return psArgs
}

// isAlpinePlatform returns true if the os version is alpine.
// If the /etc/os-release file doesn't exist, the function returns false.
func isAlpinePlatform() bool {
var osVer = ""
if util.IsExist("/etc/os-release") {
response := Run(context.TODO(), "awk", "-F '=' '{if ($1 == \"ID\") {print $2;exit 0}}' /etc/os-release")
if response.Success {
osVer = response.Result.(string)
}
}
return strings.TrimSpace(osVer) == "alpine"
}

// IsCommandAvailable return true if the command exists
func IsCommandAvailable(commandName string) bool {
response := execScript(context.TODO(), "command", fmt.Sprintf("-v %s", commandName))
return response.Success
}

//ProcessExists returns true if the pid exists, otherwise return false.
func ProcessExists(pid string) (bool, error) {
if isAlpinePlatform() {
response := Run(context.TODO(), "ps", fmt.Sprintf("-o pid | grep %s", pid))
if !response.Success {
return false, fmt.Errorf(response.Err)
}
if strings.TrimSpace(response.Result.(string)) == "" {
return false, nil
}
return true, nil
}
response := Run(context.TODO(), "ps", fmt.Sprintf("-p %s", pid))
return response.Success, nil
}

// GetPidUser returns the process user by pid
func GetPidUser(pid string) (string, error) {
var response *spec.Response
if isAlpinePlatform() {
response = Run(context.TODO(), "ps", fmt.Sprintf("-o user,pid | grep %s", pid))

} else {
response = Run(context.TODO(), "ps", fmt.Sprintf("-o user,pid -p %s | grep %s", pid, pid))
}
if !response.Success {
return "", fmt.Errorf(response.Err)
}
result := strings.TrimSpace(response.Result.(string))
if result == "" {
return "", fmt.Errorf("process user not found by pid")
}
return strings.Fields(result)[0], nil
}
39 changes: 39 additions & 0 deletions channel/local.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* 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 channel

import (
"context"

"github.com/chaosblade-io/chaosblade-spec-go/spec"
)

type LocalChannel struct {
}

// NewLocalChannel returns a local channel for invoking the host command
func NewLocalChannel() spec.Channel {
return &LocalChannel{}
}

func (l *LocalChannel) Run(ctx context.Context, script, args string) *spec.Response {
return Run(ctx, script, args)
}

func (l *LocalChannel) GetScriptPath() string {
return GetScriptPath()
}
48 changes: 48 additions & 0 deletions channel/local_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* 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 channel

import (
"context"
"fmt"
"testing"

"github.com/chaosblade-io/chaosblade-spec-go/spec"
)

// MockLocalChannel for testing
type MockLocalChannel struct {
Response *spec.Response
ScriptPath string
ExpectedCommands []string
InvokeTime int
NoCheck bool
T *testing.T
}

func (mlc *MockLocalChannel) Run(ctx context.Context, script, args string) *spec.Response {
cmd := fmt.Sprintf("%s %s", script, args)
if !mlc.NoCheck && mlc.ExpectedCommands[mlc.InvokeTime] != cmd {
mlc.T.Errorf("unexpected command: %s, expected command: %s", cmd, mlc.ExpectedCommands[mlc.InvokeTime])
}
mlc.InvokeTime++
return mlc.Response
}

func (mlc *MockLocalChannel) GetScriptPath() string {
return mlc.ScriptPath
}
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/chaosblade-io/chaosblade-spec-go

go 1.13

require (
github.com/sirupsen/logrus v1.4.2
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.2.4
)
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
30 changes: 30 additions & 0 deletions spec/channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* 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 spec

import (
"context"
)

// Channel is an interface for command invocation
type Channel interface {
// Run script with args and returns response that wraps the result
Run(ctx context.Context, script, args string) *Response

// GetScriptPath return the script path
GetScriptPath() string
}
Loading

0 comments on commit d6a4121

Please sign in to comment.