Compare commits
10 Commits
f6129f75ae
...
e780c3f2ed
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e780c3f2ed | ||
![]() |
49c91795c6 | ||
![]() |
c170be7a00 | ||
![]() |
24e06870bb | ||
![]() |
7b466bc7b4 | ||
![]() |
f0fbcc680a | ||
![]() |
9c8503082e | ||
![]() |
eddd55fdb5 | ||
![]() |
ba7372a236 | ||
![]() |
c85c491bc9 |
37
.github/e2e-linux.yml
vendored
37
.github/e2e-linux.yml
vendored
@ -1,37 +0,0 @@
|
||||
name: e2e test
|
||||
on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
jobs:
|
||||
job_1:
|
||||
name: e2e test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Go build
|
||||
run: |
|
||||
go build .
|
||||
go install .
|
||||
- name: Use goc to build self
|
||||
run: |
|
||||
./goc build -o ./gocc .
|
||||
- name: run e2e test
|
||||
run: |
|
||||
go get github.com/onsi/ginkgo/ginkgo
|
||||
make e2e
|
37
.github/e2e-wins.yml
vendored
37
.github/e2e-wins.yml
vendored
@ -1,37 +0,0 @@
|
||||
name: e2e test
|
||||
on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
jobs:
|
||||
job_1:
|
||||
name: e2e test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Go build
|
||||
run: |
|
||||
go build .
|
||||
go install .
|
||||
- name: Use goc to build self
|
||||
run: |
|
||||
.\goc.exe build -o gocc .
|
||||
- name: run e2e test
|
||||
run: |
|
||||
go get github.com/onsi/ginkgo/ginkgo
|
||||
ginkgo tests/e2e/...
|
39
.github/style_check.yml
vendored
39
.github/style_check.yml
vendored
@ -1,39 +0,0 @@
|
||||
name: style-check
|
||||
on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
jobs:
|
||||
run:
|
||||
name: vet and gofmt
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
# This step checks out a copy of your repository.
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Go vet check
|
||||
run: |
|
||||
go vet ./...
|
||||
- name: Gofmt check
|
||||
run: |
|
||||
diff=`find . -name "*.go" | xargs gofmt -s -d`
|
||||
if [[ -n "${diff}" ]]; then
|
||||
echo "Gofmt check failed :"
|
||||
echo "${diff}"
|
||||
echo "Please run this command to fix: [find . -name "*.go" | xargs gofmt -s -w]"
|
||||
exit 1
|
||||
fi
|
32
.github/ut-check-win.yml
vendored
32
.github/ut-check-win.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: unit test
|
||||
on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
jobs:
|
||||
run:
|
||||
name: go test on windows
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
# This step checks out a copy of your repository.
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Go test
|
||||
env:
|
||||
GOVERSION: ${{ matrix.go-version }}
|
||||
run: |
|
||||
go test -p 1 .\pkg\...
|
33
.github/ut-check.yml
vendored
33
.github/ut-check.yml
vendored
@ -1,33 +0,0 @@
|
||||
name: unit test
|
||||
on:
|
||||
# Trigger the workflow on push or pull request,
|
||||
# but only for the master branch
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.png'
|
||||
jobs:
|
||||
run:
|
||||
name: go test on linux
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
# This step checks out a copy of your repository.
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Go test
|
||||
env:
|
||||
GOVERSION: ${{ matrix.go-version }}
|
||||
run: |
|
||||
make test
|
||||
bash <(curl -s https://codecov.io/bash) -F unittest-$GOVERSION
|
38
.github/workflows/release.yml
vendored
38
.github/workflows/release.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.22.x
|
||||
- name: compile and release
|
||||
run: |
|
||||
./hack/release.sh
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.22.x
|
||||
- name: compile and release
|
||||
run: |
|
||||
./hack/release.sh
|
||||
@ -48,7 +48,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.22.x
|
||||
- name: compile and release
|
||||
run: |
|
||||
./hack/release.sh
|
||||
@ -57,19 +57,19 @@ jobs:
|
||||
GOARCH: amd64
|
||||
GOOS: darwin
|
||||
|
||||
# release-windows-amd64:
|
||||
# name: release windows/amd64
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@master
|
||||
# - name: Install Go
|
||||
# uses: actions/setup-go@v2
|
||||
# with:
|
||||
# go-version: 1.19.x
|
||||
# - name: compile and release
|
||||
# run: |
|
||||
# ./hack/release.sh
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# GOARCH: amd64
|
||||
# GOOS: windows
|
||||
release-windows-amd64:
|
||||
name: release windows/amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
- name: compile and release
|
||||
run: |
|
||||
./hack/release.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GOARCH: amd64
|
||||
GOOS: windows
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
tests/e2e/tmp/*
|
||||
.goc.kvstore
|
||||
coverage.txt
|
||||
.idea
|
||||
repos
|
43
cmd/build.go
43
cmd/build.go
@ -14,40 +14,43 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ar0c/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var buildCmd = &cobra.Command{
|
||||
Use: "build",
|
||||
Run: buildAction,
|
||||
Use: "build",
|
||||
Run: buildAction,
|
||||
|
||||
DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
||||
DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
||||
}
|
||||
|
||||
var (
|
||||
gocmode string
|
||||
gochost string
|
||||
gocmode string
|
||||
gochost string
|
||||
gocextra string
|
||||
)
|
||||
|
||||
func init() {
|
||||
buildCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
buildCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
buildCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
buildCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
buildCmd.Flags().StringVarP(&gocextra, "gocextra", "", "", "specify the extra information injected into the build")
|
||||
rootCmd.AddCommand(buildCmd)
|
||||
}
|
||||
|
||||
func buildAction(cmd *cobra.Command, args []string) {
|
||||
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
|
||||
b := build.NewBuild(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithBuild(),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.Build()
|
||||
b := build.NewBuild(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithBuild(),
|
||||
build.WithDebug(globalDebug),
|
||||
build.WithExtra(gocextra),
|
||||
)
|
||||
b.Build()
|
||||
|
||||
}
|
||||
|
44
cmd/inject.go
Normal file
44
cmd/inject.go
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2021 Qiniu Cloud (qiniu.com)
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"github.com/ar0c/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var injectCmd = &cobra.Command{
|
||||
Use: "inject",
|
||||
Run: injectAction,
|
||||
//DisableFlagParsing: true, // build 命令需要用原生 go 的方式处理 flags
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
injectCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
injectCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(injectCmd)
|
||||
}
|
||||
|
||||
func injectAction(cmd *cobra.Command, args []string) {
|
||||
|
||||
b := build.NewInject(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithArgs(args),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.OnlyInject()
|
||||
|
||||
}
|
@ -14,35 +14,37 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ar0c/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Run: installAction,
|
||||
Use: "install",
|
||||
Run: installAction,
|
||||
|
||||
DisableFlagParsing: true, // install 命令需要用原生 go 的方式处理 flags
|
||||
DisableFlagParsing: true, // install 命令需要用原生 go 的方式处理 flags
|
||||
}
|
||||
|
||||
func init() {
|
||||
installCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
installCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(installCmd)
|
||||
installCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
installCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
installCmd.Flags().StringVarP(&gocextra, "gocextra", "", "", "specify the extra information injected into the build")
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func installAction(cmd *cobra.Command, args []string) {
|
||||
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
|
||||
b := build.NewInstall(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithInstall(),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.Install()
|
||||
b := build.NewInstall(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithInstall(),
|
||||
build.WithDebug(globalDebug),
|
||||
build.WithExtra(gocextra),
|
||||
)
|
||||
b.Install()
|
||||
|
||||
}
|
||||
|
64
cmd/merge.go
64
cmd/merge.go
@ -14,55 +14,55 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/tools/cover"
|
||||
"k8s.io/test-infra/gopherage/pkg/cov"
|
||||
"k8s.io/test-infra/gopherage/pkg/util"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/tools/cover"
|
||||
"k8s.io/test-infra/gopherage/pkg/cov"
|
||||
"k8s.io/test-infra/gopherage/pkg/util"
|
||||
)
|
||||
|
||||
var mergeCmd = &cobra.Command{
|
||||
Use: "merge [files...]",
|
||||
Short: "Merge multiple coherent Go coverage files into a single file.",
|
||||
Long: `Merge will merge multiple Go coverage files into a single coverage file.
|
||||
Use: "merge [files...]",
|
||||
Short: "Merge multiple coherent Go coverage files into a single file.",
|
||||
Long: `Merge will merge multiple Go coverage files into a single coverage file.
|
||||
merge requires that the files are 'coherent', meaning that if they both contain references to the
|
||||
same paths, then the contents of those source files were identical for the binary that generated
|
||||
each file.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runMerge(args, outputMergeProfile)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runMerge(args, outputMergeProfile)
|
||||
},
|
||||
}
|
||||
|
||||
var outputMergeProfile string
|
||||
|
||||
func init() {
|
||||
mergeCmd.Flags().StringVarP(&outputMergeProfile, "output", "o", "mergeprofile.cov", "output file")
|
||||
mergeCmd.Flags().StringVarP(&outputMergeProfile, "output", "o", "mergeprofile.cov", "output file")
|
||||
|
||||
rootCmd.AddCommand(mergeCmd)
|
||||
rootCmd.AddCommand(mergeCmd)
|
||||
}
|
||||
|
||||
func runMerge(args []string, output string) {
|
||||
if len(args) == 0 {
|
||||
log.Fatalf("Expected at least one coverage file.")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
log.Fatalf("Expected at least one coverage file.")
|
||||
}
|
||||
|
||||
profiles := make([][]*cover.Profile, len(args))
|
||||
for _, path := range args {
|
||||
profile, err := util.LoadProfile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open %s: %v", path, err)
|
||||
}
|
||||
profiles = append(profiles, profile)
|
||||
}
|
||||
profiles := make([][]*cover.Profile, len(args))
|
||||
for _, path := range args {
|
||||
profile, err := util.LoadProfile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open %s: %v", path, err)
|
||||
}
|
||||
profiles = append(profiles, profile)
|
||||
}
|
||||
|
||||
merged, err := cov.MergeMultipleProfiles(profiles)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to merge files: %v", err)
|
||||
}
|
||||
merged, err := cov.MergeMultipleProfiles(profiles)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to merge files: %v", err)
|
||||
}
|
||||
|
||||
err = util.DumpProfile(output, merged)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to dump the merged file: %v", err)
|
||||
}
|
||||
err = util.DumpProfile(output, merged)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to dump the merged file: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -14,65 +14,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/client"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/ar0c/goc/v2/pkg/client"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var profileCmd = &cobra.Command{
|
||||
Use: "profile",
|
||||
Short: "Get coverage profile from service registry center",
|
||||
Long: `Get code coverage profile for the services under test at runtime.`,
|
||||
//Run: profile,
|
||||
Use: "profile",
|
||||
Short: "Get coverage profile from service registry center",
|
||||
Long: `Get code coverage profile for the services under test at runtime.`,
|
||||
//Run: profile,
|
||||
}
|
||||
|
||||
var (
|
||||
profileHost string
|
||||
profileOutput string // --output flag
|
||||
profileIds []string
|
||||
profileSkipPattern []string
|
||||
profileExtra string
|
||||
profileNeedPattern []string
|
||||
profileHost string
|
||||
profileOutput string // --output flag
|
||||
profileIds []string
|
||||
profileSkipPattern []string
|
||||
profileExtra string
|
||||
profileNeedPattern []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
add1Flags := func(f *pflag.FlagSet) {
|
||||
f.StringVar(&profileHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
f.StringSliceVar(&profileIds, "id", nil, "specify the ids of the services")
|
||||
f.StringVar(&profileExtra, "extra", "", "specify the regex expression of extra, only profile with extra information will be downloaded")
|
||||
}
|
||||
add1Flags := func(f *pflag.FlagSet) {
|
||||
f.StringVar(&profileHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
f.StringSliceVar(&profileIds, "id", nil, "specify the ids of the services")
|
||||
f.StringVar(&profileExtra, "extra", "", "specify the regex expression of extra, only profile with extra information will be downloaded")
|
||||
}
|
||||
|
||||
add2Flags := func(f *pflag.FlagSet) {
|
||||
f.StringVarP(&profileOutput, "output", "o", "", "download cover profile")
|
||||
f.StringSliceVar(&profileSkipPattern, "skip", nil, "skip specific packages in the profile")
|
||||
f.StringSliceVarP(&profileNeedPattern, "need", "n", nil, "find specific packages in the profile")
|
||||
}
|
||||
add2Flags := func(f *pflag.FlagSet) {
|
||||
f.StringVarP(&profileOutput, "output", "o", "", "download cover profile")
|
||||
f.StringSliceVar(&profileSkipPattern, "skip", nil, "skip specific packages in the profile")
|
||||
f.StringSliceVarP(&profileNeedPattern, "need", "n", nil, "find specific packages in the profile")
|
||||
}
|
||||
|
||||
add1Flags(getProfileCmd.Flags())
|
||||
add2Flags(getProfileCmd.Flags())
|
||||
add1Flags(getProfileCmd.Flags())
|
||||
add2Flags(getProfileCmd.Flags())
|
||||
|
||||
add1Flags(clearProfileCmd.Flags())
|
||||
add1Flags(clearProfileCmd.Flags())
|
||||
|
||||
profileCmd.AddCommand(getProfileCmd)
|
||||
profileCmd.AddCommand(clearProfileCmd)
|
||||
rootCmd.AddCommand(profileCmd)
|
||||
profileCmd.AddCommand(getProfileCmd)
|
||||
profileCmd.AddCommand(clearProfileCmd)
|
||||
rootCmd.AddCommand(profileCmd)
|
||||
}
|
||||
|
||||
var getProfileCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
Run: getProfile,
|
||||
Use: "get",
|
||||
Run: getProfile,
|
||||
}
|
||||
|
||||
func getProfile(cmd *cobra.Command, args []string) {
|
||||
client.GetProfile(profileHost, profileIds, profileSkipPattern, profileExtra, profileOutput, profileNeedPattern)
|
||||
client.GetProfile(profileHost, profileIds, profileSkipPattern, profileExtra, profileOutput, profileNeedPattern)
|
||||
}
|
||||
|
||||
var clearProfileCmd = &cobra.Command{
|
||||
Use: "clear",
|
||||
Run: clearProfile,
|
||||
Use: "clear",
|
||||
Run: clearProfile,
|
||||
}
|
||||
|
||||
func clearProfile(cmd *cobra.Command, args []string) {
|
||||
client.ClearProfile(profileHost, profileIds, profileExtra)
|
||||
client.ClearProfile(profileHost, profileIds, profileExtra)
|
||||
}
|
||||
|
32
cmd/root.go
32
cmd/root.go
@ -14,38 +14,38 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "goc",
|
||||
Short: "goc is a comprehensive coverage testing tool for go language",
|
||||
Long: `goc is a comprehensive coverage testing tool for go language.
|
||||
Use: "goc",
|
||||
Short: "goc is a comprehensive coverage testing tool for go language",
|
||||
Long: `goc is a comprehensive coverage testing tool for go language.
|
||||
|
||||
Find more information at:
|
||||
https://github.com/qiniu/goc
|
||||
`,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
//log.DisplayGoc()
|
||||
// init logger
|
||||
log.NewLogger(globalDebug)
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
//log.DisplayGoc()
|
||||
// init logger
|
||||
log.NewLogger(globalDebug)
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
log.Sync()
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
log.Sync()
|
||||
},
|
||||
}
|
||||
|
||||
var globalDebug bool
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().BoolVar(&globalDebug, "gocdebug", false, "run goc in debug mode")
|
||||
rootCmd.PersistentFlags().BoolVar(&globalDebug, "gocdebug", false, "run goc in debug mode")
|
||||
}
|
||||
|
||||
// Execute the goc tool
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
}
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
}
|
||||
}
|
||||
|
36
cmd/run.go
36
cmd/run.go
@ -14,34 +14,34 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ar0c/goc/v2/pkg/build"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Run: runAction,
|
||||
Use: "run",
|
||||
Run: runAction,
|
||||
|
||||
DisableFlagParsing: true, // run 命令需要用原生 go 的方式处理 flags
|
||||
DisableFlagParsing: true, // run 命令需要用原生 go 的方式处理 flags
|
||||
}
|
||||
|
||||
func init() {
|
||||
runCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
runCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(runCmd)
|
||||
runCmd.Flags().StringVarP(&gocmode, "gocmode", "", "count", "coverage mode: set, count, atomic, watch")
|
||||
runCmd.Flags().StringVarP(&gochost, "gochost", "", "127.0.0.1:7777", "specify the host of the goc sever")
|
||||
rootCmd.AddCommand(runCmd)
|
||||
}
|
||||
|
||||
func runAction(cmd *cobra.Command, args []string) {
|
||||
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
sets := build.CustomParseCmdAndArgs(cmd, args)
|
||||
|
||||
b := build.NewRun(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithBuild(),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.Run()
|
||||
b := build.NewRun(
|
||||
build.WithHost(gochost),
|
||||
build.WithMode(gocmode),
|
||||
build.WithFlagSets(sets),
|
||||
build.WithArgs(args),
|
||||
build.WithBuild(),
|
||||
build.WithDebug(globalDebug),
|
||||
)
|
||||
b.Run()
|
||||
}
|
||||
|
@ -14,36 +14,36 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/RickLeee/goc/v2/pkg/server"
|
||||
"github.com/RickLeee/goc/v2/pkg/server/store"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/server"
|
||||
"github.com/ar0c/goc/v2/pkg/server/store"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Start a service registry center",
|
||||
Example: "",
|
||||
Use: "server",
|
||||
Short: "Start a service registry center",
|
||||
Example: "",
|
||||
|
||||
Run: serve,
|
||||
Run: serve,
|
||||
}
|
||||
|
||||
var (
|
||||
serverHost string
|
||||
serverStore string
|
||||
serverHost string
|
||||
serverStore string
|
||||
)
|
||||
|
||||
func init() {
|
||||
serverCmd.Flags().StringVarP(&serverHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
serverCmd.Flags().StringVarP(&serverStore, "store", "", ".goc.kvstore", "specify the host of the goc server")
|
||||
serverCmd.Flags().StringVarP(&serverHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
serverCmd.Flags().StringVarP(&serverStore, "store", "", ".goc.kvstore", "specify the host of the goc server")
|
||||
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
}
|
||||
|
||||
func serve(cmd *cobra.Command, args []string) {
|
||||
s, err := store.NewFileStore(serverStore)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot create store for goc server: %v", err)
|
||||
}
|
||||
server.RunGocServerUntilExit(serverHost, s)
|
||||
s, err := store.NewFileStore(serverStore)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot create store for goc server: %v", err)
|
||||
}
|
||||
server.RunGocServerUntilExit(serverHost, s)
|
||||
}
|
||||
|
30
cmd/server_test.go
Normal file
30
cmd/server_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_serve(t *testing.T) {
|
||||
type args struct {
|
||||
cmd *cobra.Command
|
||||
args []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
args: args{},
|
||||
},
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
log.NewLogger(true)
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
serve(tt.args.cmd, tt.args.args)
|
||||
})
|
||||
}
|
||||
}
|
@ -14,61 +14,61 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/client"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/ar0c/goc/v2/pkg/client"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "Deal with the registered services",
|
||||
Long: `It can be used to list, remove the registered services.
|
||||
Use: "service",
|
||||
Short: "Deal with the registered services",
|
||||
Long: `It can be used to list, remove the registered services.
|
||||
For disconnected services, remove will delete these serivces forever,
|
||||
for connected services remove will force these services register again.`,
|
||||
}
|
||||
|
||||
var (
|
||||
listHost string
|
||||
listWide bool
|
||||
listIds []string
|
||||
listJson bool
|
||||
listHost string
|
||||
listWide bool
|
||||
listIds []string
|
||||
listJson bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
add1Flags := func(f *pflag.FlagSet) {
|
||||
f.StringVar(&listHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
f.BoolVar(&listWide, "wide", false, "list all services with more information (such as pid)")
|
||||
f.BoolVar(&listJson, "json", false, "list all services info as json format")
|
||||
f.StringSliceVar(&listIds, "id", nil, "specify the ids of the services")
|
||||
}
|
||||
add1Flags := func(f *pflag.FlagSet) {
|
||||
f.StringVar(&listHost, "host", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
f.BoolVar(&listWide, "wide", false, "list all services with more information (such as pid)")
|
||||
f.BoolVar(&listJson, "json", false, "list all services info as json format")
|
||||
f.StringSliceVar(&listIds, "id", nil, "specify the ids of the services")
|
||||
}
|
||||
|
||||
add1Flags(getServiceCmd.Flags())
|
||||
add1Flags(deleteServiceCmd.Flags())
|
||||
add1Flags(getServiceCmd.Flags())
|
||||
add1Flags(deleteServiceCmd.Flags())
|
||||
|
||||
listCmd.AddCommand(getServiceCmd)
|
||||
listCmd.AddCommand(deleteServiceCmd)
|
||||
rootCmd.AddCommand(listCmd)
|
||||
listCmd.AddCommand(getServiceCmd)
|
||||
listCmd.AddCommand(deleteServiceCmd)
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
|
||||
func list(cmd *cobra.Command, args []string) {
|
||||
client.ListAgents(listHost, listIds, listWide, listJson)
|
||||
client.ListAgents(listHost, listIds, listWide, listJson)
|
||||
}
|
||||
|
||||
var getServiceCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
Run: getAgents,
|
||||
Use: "get",
|
||||
Run: getAgents,
|
||||
}
|
||||
|
||||
func getAgents(cmd *cobra.Command, args []string) {
|
||||
client.ListAgents(listHost, listIds, listWide, listJson)
|
||||
client.ListAgents(listHost, listIds, listWide, listJson)
|
||||
}
|
||||
|
||||
var deleteServiceCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Run: deleteAgents,
|
||||
Use: "delete",
|
||||
Run: deleteAgents,
|
||||
}
|
||||
|
||||
func deleteAgents(cmd *cobra.Command, args []string) {
|
||||
client.DeleteAgents(listHost, listIds)
|
||||
client.DeleteAgents(listHost, listIds)
|
||||
}
|
||||
|
22
cmd/watch.go
22
cmd/watch.go
@ -14,28 +14,28 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
cli "github.com/RickLeee/goc/v2/pkg/watch"
|
||||
"github.com/spf13/cobra"
|
||||
cli "github.com/ar0c/goc/v2/pkg/watch"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var watchCmd = &cobra.Command{
|
||||
Use: "watch",
|
||||
Short: "watch for profile's real time update",
|
||||
Long: "watch for profile's real time update",
|
||||
Example: "",
|
||||
Use: "watch",
|
||||
Short: "watch for profile's real time update",
|
||||
Long: "watch for profile's real time update",
|
||||
Example: "",
|
||||
|
||||
Run: watch,
|
||||
Run: watch,
|
||||
}
|
||||
|
||||
var (
|
||||
watchHost string
|
||||
watchHost string
|
||||
)
|
||||
|
||||
func init() {
|
||||
watchCmd.Flags().StringVarP(&watchHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
rootCmd.AddCommand(watchCmd)
|
||||
watchCmd.Flags().StringVarP(&watchHost, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
|
||||
rootCmd.AddCommand(watchCmd)
|
||||
}
|
||||
|
||||
func watch(cmd *cobra.Command, args []string) {
|
||||
cli.Watch(watchHost)
|
||||
cli.Watch(watchHost)
|
||||
}
|
||||
|
21
go.mod
21
go.mod
@ -1,6 +1,8 @@
|
||||
module github.com/RickLeee/goc/v2
|
||||
module github.com/ar0c/goc/v2
|
||||
|
||||
go 1.20
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.5
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.7.2
|
||||
@ -17,9 +19,9 @@ require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tongjingran/copy v1.4.2
|
||||
go.uber.org/zap v1.17.0
|
||||
golang.org/x/mod v0.4.2
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
|
||||
golang.org/x/tools v0.1.3
|
||||
golang.org/x/mod v0.21.0
|
||||
golang.org/x/term v0.12.0
|
||||
golang.org/x/tools v0.13.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
k8s.io/kubectl v0.21.2
|
||||
k8s.io/test-infra v0.0.0-20210618100605-34aa2f2aa75b
|
||||
@ -54,13 +56,12 @@ require (
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
24
go.sum
24
go.sum
@ -1320,8 +1320,9 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -1360,8 +1361,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1417,8 +1418,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -1524,12 +1526,13 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1538,8 +1541,9 @@ golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5f
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -1648,8 +1652,8 @@ golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4X
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -10,7 +10,7 @@ RELEASE_VERSION=$(echo $EVENT_DATA | jq -r .release.tag_name)
|
||||
PROJECT_NAME=$(basename $GITHUB_REPOSITORY)
|
||||
NAME="${NAME:-${PROJECT_NAME}-${RELEASE_VERSION}}-${GOOS}-${GOARCH}"
|
||||
|
||||
CGO_ENABLED=0 go build -o goc -ldflags "-X 'github.com/RickLeee/goc/v2/cmd.Version=${RELEASE_VERSION}'" .
|
||||
CGO_ENABLED=0 go build -o goc -ldflags "-X 'github.com/ar0c/goc/v2/cmd.Version=${RELEASE_VERSION}'" .
|
||||
|
||||
ARCHIVE=tmp.tar.gz
|
||||
FILE_LIST=goc
|
||||
|
4
main.go
4
main.go
@ -14,9 +14,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/cmd"
|
||||
"github.com/ar0c/goc/v2/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
cmd.Execute()
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ var (
|
||||
token string
|
||||
id string
|
||||
cond = sync.NewCond(&sync.Mutex{})
|
||||
register_extra = ""
|
||||
commitID string = "{{.CommitID}}"
|
||||
branch string = "{{.Branch}}"
|
||||
register_extra = "{{.Extra}}"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -57,6 +57,11 @@ func init() {
|
||||
host = host_env
|
||||
}
|
||||
|
||||
// init extra information
|
||||
if os.Getenv("GOC_REGISTER_EXTRA") != "" {
|
||||
register_extra = os.Getenv("GOC_REGISTER_EXTRA")
|
||||
}
|
||||
|
||||
var dialer = websocket.DefaultDialer
|
||||
|
||||
go func() {
|
||||
|
@ -14,62 +14,87 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Build struct a build
|
||||
type Build struct {
|
||||
Args []string // all goc + go command line args + flags
|
||||
FlagSets *pflag.FlagSet
|
||||
BuildType int
|
||||
Args []string // all goc + go command line args + flags
|
||||
FlagSets *pflag.FlagSet
|
||||
BuildType int
|
||||
|
||||
Debug bool
|
||||
Host string
|
||||
Mode string // cover mode
|
||||
Debug bool
|
||||
Host string
|
||||
Mode string // cover mode
|
||||
Extra string
|
||||
|
||||
GOPATH string
|
||||
GOBIN string
|
||||
CurWd string
|
||||
TmpWd string
|
||||
CurModProjectDir string
|
||||
TmpModProjectDir string
|
||||
GOPATH string
|
||||
GOBIN string
|
||||
CurWd string
|
||||
TmpWd string
|
||||
CurModProjectDir string
|
||||
TmpModProjectDir string
|
||||
|
||||
Goflags []string // go command line flags
|
||||
GoArgs []string // go command line args
|
||||
Packages []string // go command line [packages]
|
||||
Goflags []string // go command line flags
|
||||
GoArgs []string // go command line args
|
||||
Packages []string // go command line [packages]
|
||||
|
||||
IsVendorMod bool // vendor, readonly, or mod?
|
||||
IsModEdit bool // is mod file edited?
|
||||
IsVendorMod bool // vendor, readonly, or mod?
|
||||
IsModEdit bool // is mod file edited?
|
||||
|
||||
ImportPath string // the whole import path of the project
|
||||
Pkgs map[string]*Package
|
||||
GlobalCoverVarImportPath string
|
||||
GlobalCoverVarImportPathDir string
|
||||
ImportPath string // the whole import path of the project
|
||||
Pkgs map[string]*Package
|
||||
GlobalCoverVarImportPath string
|
||||
GlobalCoverVarImportPathDir string
|
||||
}
|
||||
|
||||
// NewBuild creates a Build struct
|
||||
func NewBuild(opts ...gocOption) *Build {
|
||||
b := &Build{}
|
||||
b := &Build{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
b.buildCmdArgsParse()
|
||||
// 2. 解析 go 包位置
|
||||
b.getPackagesDir()
|
||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||
b.readProjectMetaInfo()
|
||||
// 4. 展示元信息
|
||||
b.displayProjectMetaInfo()
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
b.buildCmdArgsParse()
|
||||
// 2. 解析 go 包位置
|
||||
b.getPackagesDir()
|
||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||
b.readProjectMetaInfo()
|
||||
// 4. 展示元信息
|
||||
b.displayProjectMetaInfo()
|
||||
|
||||
return b
|
||||
return b
|
||||
}
|
||||
|
||||
func NewInject(opts ...gocOption) *Build {
|
||||
b := &Build{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
b.CurWd = curWd
|
||||
|
||||
//// 1. 解析 goc 命令行和 go 命令行
|
||||
//b.buildCmdArgsParse()
|
||||
// 2. 解析 go 包位置
|
||||
b.getPackagesDir()
|
||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||
b.readProjectMetaInfo(true)
|
||||
// 4. 展示元信息
|
||||
b.displayProjectMetaInfo()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Build starts go build
|
||||
@ -78,69 +103,81 @@ func NewBuild(opts ...gocOption) *Build {
|
||||
// 2. inject cover variables and functions into the project,
|
||||
// 3. build the project in temp.
|
||||
func (b *Build) Build() {
|
||||
// 1. 拷贝至临时目录
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
// 1. 拷贝至临时目录
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
|
||||
log.Donef("project copied to temporary directory")
|
||||
log.Donef("project copied to temporary directory")
|
||||
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
|
||||
// 4. build in the temp project
|
||||
b.doBuildInTemp()
|
||||
// 4. build in the temp project
|
||||
b.doBuildInTemp()
|
||||
}
|
||||
|
||||
func (b *Build) OnlyInject() {
|
||||
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Build) doBuildInTemp() {
|
||||
log.StartWait("building the injected project")
|
||||
log.StartWait("building the injected project")
|
||||
|
||||
goflags := b.Goflags
|
||||
// 检查用户是否自定义了 -o
|
||||
oSet := false
|
||||
for _, flag := range goflags {
|
||||
if flag == "-o" {
|
||||
oSet = true
|
||||
}
|
||||
}
|
||||
goflags := b.Goflags
|
||||
// 检查用户是否自定义了 -o
|
||||
oSet := false
|
||||
for _, flag := range goflags {
|
||||
if flag == "-o" {
|
||||
oSet = true
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没被设置就加一个至原命令执行的目录
|
||||
if !oSet {
|
||||
goflags = append(goflags, "-o", b.CurWd)
|
||||
}
|
||||
// 如果没被设置就加一个至原命令执行的目录
|
||||
if !oSet {
|
||||
goflags = append(goflags, "-o", b.CurWd)
|
||||
}
|
||||
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
|
||||
pacakges := b.Packages
|
||||
pacakges := b.Packages
|
||||
|
||||
goflags = append(goflags, pacakges...)
|
||||
goflags = append(goflags, pacakges...)
|
||||
|
||||
args := []string{"build"}
|
||||
args = append(args, goflags...)
|
||||
// go 命令行由 go build [-o output] [build flags] [packages] 组成
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
args := []string{"build"}
|
||||
args = append(args, goflags...)
|
||||
// go 命令行由 go build [-o output] [build flags] [packages] 组成
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
log.Infof("go build cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go build: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("fail to execute go build: %v", err)
|
||||
}
|
||||
log.Infof("go build cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go build: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("fail to execute go build: %v", err)
|
||||
}
|
||||
|
||||
// done
|
||||
log.StopWait()
|
||||
log.Donef("go build done")
|
||||
// done
|
||||
log.StopWait()
|
||||
log.Donef("go build done")
|
||||
}
|
||||
|
||||
// nicePrintArgs 优化 args 打印内容
|
||||
@ -149,32 +186,32 @@ func (b *Build) doBuildInTemp() {
|
||||
//
|
||||
// 实际输出会变为:go build -ldflags -X my/package/config.Version=1.0.0 -o /home/lyy/gitdown/gin-test/cmd .
|
||||
func nicePrintArgs(args []string) []string {
|
||||
output := make([]string, 0)
|
||||
for _, arg := range args {
|
||||
if strings.Contains(arg, " ") {
|
||||
output = append(output, "\""+arg+"\"")
|
||||
} else {
|
||||
output = append(output, arg)
|
||||
}
|
||||
}
|
||||
output := make([]string, 0)
|
||||
for _, arg := range args {
|
||||
if strings.Contains(arg, " ") {
|
||||
output = append(output, "\""+arg+"\"")
|
||||
} else {
|
||||
output = append(output, arg)
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
return output
|
||||
}
|
||||
|
||||
func (b *Build) reVendor() {
|
||||
log.StartWait("re-vendoring the project")
|
||||
cmd := exec.Command("go", "mod", "vendor")
|
||||
cmd.Dir = b.TmpModProjectDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
log.StartWait("re-vendoring the project")
|
||||
cmd := exec.Command("go", "mod", "vendor")
|
||||
cmd.Dir = b.TmpModProjectDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go vendor: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("fail to execute go vendor: %v", err)
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go vendor: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("fail to execute go vendor: %v", err)
|
||||
}
|
||||
|
||||
log.StopWait()
|
||||
log.Donef("re-vendor the project done")
|
||||
log.StopWait()
|
||||
log.Donef("re-vendor the project done")
|
||||
}
|
||||
|
@ -14,15 +14,15 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var buildUsage string = `Usage:
|
||||
@ -53,23 +53,23 @@ However, other flags' order are same with the go official command.
|
||||
`
|
||||
|
||||
const (
|
||||
GO_BUILD = iota
|
||||
GO_INSTALL
|
||||
GO_BUILD = iota
|
||||
GO_INSTALL
|
||||
)
|
||||
|
||||
// CustomParseCmdAndArgs 因为关闭了 cobra 的解析功能,需要手动构造并解析 goc flags
|
||||
func CustomParseCmdAndArgs(cmd *cobra.Command, args []string) *pflag.FlagSet {
|
||||
// 首先解析 cobra 定义的 flag
|
||||
allFlagSets := cmd.Flags()
|
||||
// 因为 args 里面含有 go 的 flag,所以需要忽略解析 go flag 的错误
|
||||
allFlagSets.Init("GOC", pflag.ContinueOnError)
|
||||
// 忽略 go flag 在 goc 中的解析错误
|
||||
allFlagSets.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{
|
||||
UnknownFlags: true,
|
||||
}
|
||||
allFlagSets.Parse(args)
|
||||
// 首先解析 cobra 定义的 flag
|
||||
allFlagSets := cmd.Flags()
|
||||
// 因为 args 里面含有 go 的 flag,所以需要忽略解析 go flag 的错误
|
||||
allFlagSets.Init("GOC", pflag.ContinueOnError)
|
||||
// 忽略 go flag 在 goc 中的解析错误
|
||||
allFlagSets.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{
|
||||
UnknownFlags: true,
|
||||
}
|
||||
allFlagSets.Parse(args)
|
||||
|
||||
return allFlagSets
|
||||
return allFlagSets
|
||||
}
|
||||
|
||||
// buildCmdArgsParse parse both go flags and goc flags, it rewrite go flags if
|
||||
@ -77,246 +77,246 @@ func CustomParseCmdAndArgs(cmd *cobra.Command, args []string) *pflag.FlagSet {
|
||||
//
|
||||
// 吞下 [packages] 之前所有的 flags.
|
||||
func (b *Build) buildCmdArgsParse() {
|
||||
args := b.Args
|
||||
cmdType := b.BuildType
|
||||
allFlagSets := b.FlagSets
|
||||
args := b.Args
|
||||
cmdType := b.BuildType
|
||||
allFlagSets := b.FlagSets
|
||||
|
||||
// 重写 help
|
||||
helpFlag := allFlagSets.Lookup("help")
|
||||
// 重写 help
|
||||
helpFlag := allFlagSets.Lookup("help")
|
||||
|
||||
if helpFlag.Changed {
|
||||
if cmdType == GO_BUILD {
|
||||
printGoHelp(buildUsage)
|
||||
} else if cmdType == GO_INSTALL {
|
||||
printGoHelp(installUsage)
|
||||
}
|
||||
if helpFlag.Changed {
|
||||
if cmdType == GO_BUILD {
|
||||
printGoHelp(buildUsage)
|
||||
} else if cmdType == GO_INSTALL {
|
||||
printGoHelp(installUsage)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
// 删除 help flag
|
||||
args = findAndDelHelpFlag(args)
|
||||
os.Exit(0)
|
||||
}
|
||||
// 删除 help flag
|
||||
args = findAndDelHelpFlag(args)
|
||||
|
||||
// 必须手动调用
|
||||
// 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值
|
||||
log.NewLogger(b.Debug)
|
||||
// 必须手动调用
|
||||
// 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值
|
||||
log.NewLogger(b.Debug)
|
||||
|
||||
// 删除 cobra 定义的 flag
|
||||
allFlagSets.Visit(func(f *pflag.Flag) {
|
||||
args = findAndDelGocFlag(args, f.Name, f.Value.String())
|
||||
})
|
||||
// 删除 cobra 定义的 flag
|
||||
allFlagSets.Visit(func(f *pflag.Flag) {
|
||||
args = findAndDelGocFlag(args, f.Name, f.Value.String())
|
||||
})
|
||||
|
||||
// 然后解析 go 的 flag
|
||||
goFlagSets := flag.NewFlagSet("GO", flag.ContinueOnError)
|
||||
addBuildFlags(goFlagSets)
|
||||
addOutputFlags(goFlagSets)
|
||||
err := goFlagSets.Parse(args)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
// 然后解析 go 的 flag
|
||||
goFlagSets := flag.NewFlagSet("GO", flag.ContinueOnError)
|
||||
addBuildFlags(goFlagSets)
|
||||
addOutputFlags(goFlagSets)
|
||||
err := goFlagSets.Parse(args)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// 找出设置的 go flag
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
flags := make([]string, 0)
|
||||
goFlagSets.Visit(func(f *flag.Flag) {
|
||||
// 将用户指定 -o 改成绝对目录
|
||||
if f.Name == "o" {
|
||||
outputDir := f.Value.String()
|
||||
outputDir, err := filepath.Abs(outputDir)
|
||||
if err != nil {
|
||||
log.Fatalf("output flag is not valid: %v", err)
|
||||
}
|
||||
flags = append(flags, "-o", outputDir)
|
||||
} else {
|
||||
if _, ok := booleanFlags[f.Name]; !ok {
|
||||
flags = append(flags, "-"+f.Name, f.Value.String())
|
||||
} else {
|
||||
flags = append(flags, "-"+f.Name)
|
||||
}
|
||||
if f.Name == "mod" {
|
||||
if f.Value.String() == "vendor" {
|
||||
b.IsVendorMod = true
|
||||
} else {
|
||||
b.IsVendorMod = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 找出设置的 go flag
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
flags := make([]string, 0)
|
||||
goFlagSets.Visit(func(f *flag.Flag) {
|
||||
// 将用户指定 -o 改成绝对目录
|
||||
if f.Name == "o" {
|
||||
outputDir := f.Value.String()
|
||||
outputDir, err := filepath.Abs(outputDir)
|
||||
if err != nil {
|
||||
log.Fatalf("output flag is not valid: %v", err)
|
||||
}
|
||||
flags = append(flags, "-o", outputDir)
|
||||
} else {
|
||||
if _, ok := booleanFlags[f.Name]; !ok {
|
||||
flags = append(flags, "-"+f.Name, f.Value.String())
|
||||
} else {
|
||||
flags = append(flags, "-"+f.Name)
|
||||
}
|
||||
if f.Name == "mod" {
|
||||
if f.Value.String() == "vendor" {
|
||||
b.IsVendorMod = true
|
||||
} else {
|
||||
b.IsVendorMod = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Goflags = flags
|
||||
b.CurWd = curWd
|
||||
b.GoArgs = goFlagSets.Args()
|
||||
return
|
||||
b.Goflags = flags
|
||||
b.CurWd = curWd
|
||||
b.GoArgs = goFlagSets.Args()
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Build) runCmdArgsParse() {
|
||||
args := b.Args
|
||||
allFlagSets := b.FlagSets
|
||||
args := b.Args
|
||||
allFlagSets := b.FlagSets
|
||||
|
||||
// 重写 help
|
||||
helpFlag := allFlagSets.Lookup("help")
|
||||
// 重写 help
|
||||
helpFlag := allFlagSets.Lookup("help")
|
||||
|
||||
if helpFlag.Changed {
|
||||
printGoHelp(runUsage)
|
||||
os.Exit(0)
|
||||
}
|
||||
if helpFlag.Changed {
|
||||
printGoHelp(runUsage)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// 删除 help flag
|
||||
args = findAndDelHelpFlag(args)
|
||||
// 删除 help flag
|
||||
args = findAndDelHelpFlag(args)
|
||||
|
||||
// 必须手动调用
|
||||
// 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值
|
||||
log.NewLogger(b.Debug)
|
||||
// 必须手动调用
|
||||
// 由于关闭了 cobra 的 flag parse,root PersistentPreRun 调用时,log.NewLogger 并没有拿到 debug 值
|
||||
log.NewLogger(b.Debug)
|
||||
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
b.CurWd = curWd
|
||||
curWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get current working directory: %v", err)
|
||||
}
|
||||
b.CurWd = curWd
|
||||
|
||||
// 获取除 goc flags 之外的 args
|
||||
// 删除 cobra 定义的 flag
|
||||
allFlagSets.Visit(func(f *pflag.Flag) {
|
||||
args = findAndDelGocFlag(args, f.Name, f.Value.String())
|
||||
})
|
||||
// 获取除 goc flags 之外的 args
|
||||
// 删除 cobra 定义的 flag
|
||||
allFlagSets.Visit(func(f *pflag.Flag) {
|
||||
args = findAndDelGocFlag(args, f.Name, f.Value.String())
|
||||
})
|
||||
|
||||
b.GoArgs = args
|
||||
b.GoArgs = args
|
||||
}
|
||||
|
||||
func findAndDelGocFlag(a []string, x string, v string) []string {
|
||||
new := make([]string, 0, len(a))
|
||||
x = "--" + x
|
||||
x_v := x + "=" + v
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] == "--gocdebug" {
|
||||
// debug 是 bool,就一个元素
|
||||
continue
|
||||
} else if a[i] == x {
|
||||
// 有 goc flag 长这样 --mode watch
|
||||
i++
|
||||
continue
|
||||
} else if a[i] == x_v {
|
||||
// 有 goc flag 长这样 --mode=watch
|
||||
continue
|
||||
} else {
|
||||
// 剩下的是 go flag
|
||||
new = append(new, a[i])
|
||||
}
|
||||
}
|
||||
new := make([]string, 0, len(a))
|
||||
x = "--" + x
|
||||
x_v := x + "=" + v
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] == "--gocdebug" {
|
||||
// debug 是 bool,就一个元素
|
||||
continue
|
||||
} else if a[i] == x {
|
||||
// 有 goc flag 长这样 --mode watch
|
||||
i++
|
||||
continue
|
||||
} else if a[i] == x_v {
|
||||
// 有 goc flag 长这样 --mode=watch
|
||||
continue
|
||||
} else {
|
||||
// 剩下的是 go flag
|
||||
new = append(new, a[i])
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
return new
|
||||
}
|
||||
|
||||
func findAndDelHelpFlag(a []string) []string {
|
||||
new := make([]string, 0, len(a))
|
||||
for _, v := range a {
|
||||
if v == "--help" || v == "-h" {
|
||||
continue
|
||||
} else {
|
||||
new = append(new, v)
|
||||
}
|
||||
}
|
||||
new := make([]string, 0, len(a))
|
||||
for _, v := range a {
|
||||
if v == "--help" || v == "-h" {
|
||||
continue
|
||||
} else {
|
||||
new = append(new, v)
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
return new
|
||||
}
|
||||
|
||||
type goConfig struct {
|
||||
BuildA bool
|
||||
BuildBuildmode string // -buildmode flag
|
||||
BuildMod string // -mod flag
|
||||
BuildModReason string // reason -mod flag is set, if set by default
|
||||
BuildI bool // -i flag
|
||||
BuildLinkshared bool // -linkshared flag
|
||||
BuildMSan bool // -msan flag
|
||||
BuildN bool // -n flag
|
||||
BuildO string // -o flag
|
||||
BuildP int // -p flag
|
||||
BuildPkgdir string // -pkgdir flag
|
||||
BuildRace bool // -race flag
|
||||
BuildToolexec string // -toolexec flag
|
||||
BuildToolchainName string
|
||||
BuildToolchainCompiler func() string
|
||||
BuildToolchainLinker func() string
|
||||
BuildTrimpath bool // -trimpath flag
|
||||
BuildV bool // -v flag
|
||||
BuildWork bool // -work flag
|
||||
BuildX bool // -x flag
|
||||
// from buildcontext
|
||||
Installsuffix string // -installSuffix
|
||||
BuildTags string // -tags
|
||||
// from load
|
||||
BuildAsmflags string
|
||||
BuildCompiler string
|
||||
BuildGcflags string
|
||||
BuildGccgoflags string
|
||||
BuildLdflags string
|
||||
BuildA bool
|
||||
BuildBuildmode string // -buildmode flag
|
||||
BuildMod string // -mod flag
|
||||
BuildModReason string // reason -mod flag is set, if set by default
|
||||
BuildI bool // -i flag
|
||||
BuildLinkshared bool // -linkshared flag
|
||||
BuildMSan bool // -msan flag
|
||||
BuildN bool // -n flag
|
||||
BuildO string // -o flag
|
||||
BuildP int // -p flag
|
||||
BuildPkgdir string // -pkgdir flag
|
||||
BuildRace bool // -race flag
|
||||
BuildToolexec string // -toolexec flag
|
||||
BuildToolchainName string
|
||||
BuildToolchainCompiler func() string
|
||||
BuildToolchainLinker func() string
|
||||
BuildTrimpath bool // -trimpath flag
|
||||
BuildV bool // -v flag
|
||||
BuildWork bool // -work flag
|
||||
BuildX bool // -x flag
|
||||
// from buildcontext
|
||||
Installsuffix string // -installSuffix
|
||||
BuildTags string // -tags
|
||||
// from load
|
||||
BuildAsmflags string
|
||||
BuildCompiler string
|
||||
BuildGcflags string
|
||||
BuildGccgoflags string
|
||||
BuildLdflags string
|
||||
|
||||
// mod related
|
||||
ModCacheRW bool
|
||||
ModFile string
|
||||
// mod related
|
||||
ModCacheRW bool
|
||||
ModFile string
|
||||
}
|
||||
|
||||
var goflags goConfig
|
||||
var booleanFlags map[string]struct{} = make(map[string]struct{})
|
||||
|
||||
func addBuildFlags(cmdSet *flag.FlagSet) {
|
||||
cmdSet.BoolVar(&goflags.BuildA, "a", false, "")
|
||||
booleanFlags["a"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildN, "n", false, "")
|
||||
booleanFlags["n"] = struct{}{}
|
||||
cmdSet.IntVar(&goflags.BuildP, "p", 4, "")
|
||||
cmdSet.BoolVar(&goflags.BuildV, "v", false, "")
|
||||
booleanFlags["v"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildX, "x", false, "")
|
||||
booleanFlags["x"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.BuildBuildmode, "buildmode", "default", "")
|
||||
cmdSet.StringVar(&goflags.BuildMod, "mod", "", "")
|
||||
cmdSet.StringVar(&goflags.Installsuffix, "installsuffix", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildA, "a", false, "")
|
||||
booleanFlags["a"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildN, "n", false, "")
|
||||
booleanFlags["n"] = struct{}{}
|
||||
cmdSet.IntVar(&goflags.BuildP, "p", 4, "")
|
||||
cmdSet.BoolVar(&goflags.BuildV, "v", false, "")
|
||||
booleanFlags["v"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildX, "x", false, "")
|
||||
booleanFlags["x"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.BuildBuildmode, "buildmode", "default", "")
|
||||
cmdSet.StringVar(&goflags.BuildMod, "mod", "", "")
|
||||
cmdSet.StringVar(&goflags.Installsuffix, "installsuffix", "", "")
|
||||
|
||||
// 类型和 go 原生的不一样,这里纯粹是为了 parse 并传递给 go
|
||||
cmdSet.StringVar(&goflags.BuildAsmflags, "asmflags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildCompiler, "compiler", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildGcflags, "gcflags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildGccgoflags, "gccgoflags", "", "")
|
||||
// mod related
|
||||
cmdSet.BoolVar(&goflags.ModCacheRW, "modcacherw", false, "")
|
||||
booleanFlags["modcacherw"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.ModFile, "modfile", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildLdflags, "ldflags", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildLinkshared, "linkshared", false, "")
|
||||
booleanFlags["linkshared"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.BuildPkgdir, "pkgdir", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildRace, "race", false, "")
|
||||
booleanFlags["race"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildMSan, "msan", false, "")
|
||||
booleanFlags["msan"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.BuildTags, "tags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildToolexec, "toolexec", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildTrimpath, "trimpath", false, "")
|
||||
booleanFlags["trimpath"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildWork, "work", false, "")
|
||||
booleanFlags["work"] = struct{}{}
|
||||
// 类型和 go 原生的不一样,这里纯粹是为了 parse 并传递给 go
|
||||
cmdSet.StringVar(&goflags.BuildAsmflags, "asmflags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildCompiler, "compiler", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildGcflags, "gcflags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildGccgoflags, "gccgoflags", "", "")
|
||||
// mod related
|
||||
cmdSet.BoolVar(&goflags.ModCacheRW, "modcacherw", false, "")
|
||||
booleanFlags["modcacherw"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.ModFile, "modfile", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildLdflags, "ldflags", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildLinkshared, "linkshared", false, "")
|
||||
booleanFlags["linkshared"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.BuildPkgdir, "pkgdir", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildRace, "race", false, "")
|
||||
booleanFlags["race"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildMSan, "msan", false, "")
|
||||
booleanFlags["msan"] = struct{}{}
|
||||
cmdSet.StringVar(&goflags.BuildTags, "tags", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildToolexec, "toolexec", "", "")
|
||||
cmdSet.BoolVar(&goflags.BuildTrimpath, "trimpath", false, "")
|
||||
booleanFlags["trimpath"] = struct{}{}
|
||||
cmdSet.BoolVar(&goflags.BuildWork, "work", false, "")
|
||||
booleanFlags["work"] = struct{}{}
|
||||
}
|
||||
|
||||
func addOutputFlags(cmdSet *flag.FlagSet) {
|
||||
cmdSet.StringVar(&goflags.BuildO, "o", "", "")
|
||||
cmdSet.StringVar(&goflags.BuildO, "o", "", "")
|
||||
}
|
||||
|
||||
func printGoHelp(usage string) {
|
||||
fmt.Println(usage)
|
||||
fmt.Println(usage)
|
||||
}
|
||||
|
||||
func printGocHelp(cmd *cobra.Command) {
|
||||
flags := cmd.LocalFlags()
|
||||
globalFlags := cmd.Parent().PersistentFlags()
|
||||
flags := cmd.LocalFlags()
|
||||
globalFlags := cmd.Parent().PersistentFlags()
|
||||
|
||||
fmt.Println("Flags:")
|
||||
fmt.Println(flags.FlagUsages())
|
||||
fmt.Println("Flags:")
|
||||
fmt.Println(flags.FlagUsages())
|
||||
|
||||
fmt.Println("Global Flags:")
|
||||
fmt.Println(globalFlags.FlagUsages())
|
||||
fmt.Println("Global Flags:")
|
||||
fmt.Println(globalFlags.FlagUsages())
|
||||
}
|
||||
|
||||
// GetPackagesDir parse [pacakges] part of args, it will fatal if error encountered
|
||||
@ -328,43 +328,43 @@ func printGocHelp(cmd *cobra.Command) {
|
||||
// 如果 [packages] 非法(即不符合 go 原生的定义),则返回对应错误
|
||||
// 这里只考虑 go mod 的方式
|
||||
func (b *Build) getPackagesDir() {
|
||||
patterns := b.GoArgs
|
||||
packages := make([]string, 0)
|
||||
for _, p := range patterns {
|
||||
// patterns 只支持两种格式
|
||||
// 1. 要么是直接指向某些 .go 文件的相对/绝对路径
|
||||
if strings.HasSuffix(p, ".go") {
|
||||
if fi, err := os.Stat(p); err == nil && !fi.IsDir() {
|
||||
// check if valid
|
||||
if err := goFilesPackage(patterns); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
patterns := b.GoArgs
|
||||
packages := make([]string, 0)
|
||||
for _, p := range patterns {
|
||||
// patterns 只支持两种格式
|
||||
// 1. 要么是直接指向某些 .go 文件的相对/绝对路径
|
||||
if strings.HasSuffix(p, ".go") {
|
||||
if fi, err := os.Stat(p); err == nil && !fi.IsDir() {
|
||||
// check if valid
|
||||
if err := goFilesPackage(patterns); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
// 获取相对于 current working directory 对路径
|
||||
for _, p := range patterns {
|
||||
if filepath.IsAbs(p) {
|
||||
relPath, err := filepath.Rel(b.CurWd, p)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get [packages] relative path from current working directory: %v", err)
|
||||
}
|
||||
packages = append(packages, relPath)
|
||||
} else {
|
||||
packages = append(packages, p)
|
||||
}
|
||||
}
|
||||
// fix: go build ./xx/main.go 需要转换为
|
||||
// go build ./xx/main.go ./xx/goc-cover-agent-apis-auto-generated-11111-22222-bridge.go
|
||||
dir := filepath.Dir(packages[0])
|
||||
packages = append(packages, filepath.Join(dir, "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"))
|
||||
b.Packages = packages
|
||||
// 获取相对于 current working directory 对路径
|
||||
for _, p := range patterns {
|
||||
if filepath.IsAbs(p) {
|
||||
relPath, err := filepath.Rel(b.CurWd, p)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get [packages] relative path from current working directory: %v", err)
|
||||
}
|
||||
packages = append(packages, relPath)
|
||||
} else {
|
||||
packages = append(packages, p)
|
||||
}
|
||||
}
|
||||
// fix: go build ./xx/main.go 需要转换为
|
||||
// go build ./xx/main.go ./xx/goc-cover-agent-apis-auto-generated-11111-22222-bridge.go
|
||||
dir := filepath.Dir(packages[0])
|
||||
packages = append(packages, filepath.Join(dir, "goc-cover-agent-apis-auto-generated-11111-22222-bridge.go"))
|
||||
b.Packages = packages
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 要么是 import path
|
||||
b.Packages = patterns
|
||||
// 2. 要么是 import path
|
||||
b.Packages = patterns
|
||||
}
|
||||
|
||||
// goFilesPackage 对一组 go 文件解析,判断是否合法
|
||||
@ -373,40 +373,40 @@ func (b *Build) getPackagesDir() {
|
||||
// 2. *.go 文件都在同一个目录?
|
||||
// 3. *.go 文件存在?
|
||||
func goFilesPackage(gofiles []string) error {
|
||||
// 1. 必须都是 *.go 结尾
|
||||
for _, f := range gofiles {
|
||||
if !strings.HasSuffix(f, ".go") {
|
||||
return fmt.Errorf("named files must be .go files: %s", f)
|
||||
}
|
||||
}
|
||||
// 1. 必须都是 *.go 结尾
|
||||
for _, f := range gofiles {
|
||||
if !strings.HasSuffix(f, ".go") {
|
||||
return fmt.Errorf("named files must be .go files: %s", f)
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
for _, file := range gofiles {
|
||||
// 3. 文件都存在?
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dir string
|
||||
for _, file := range gofiles {
|
||||
// 3. 文件都存在?
|
||||
fi, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2.1 有可能以 *.go 结尾的目录
|
||||
if fi.IsDir() {
|
||||
return fmt.Errorf("%s is a directory, should be a Go file", file)
|
||||
}
|
||||
// 2.1 有可能以 *.go 结尾的目录
|
||||
if fi.IsDir() {
|
||||
return fmt.Errorf("%s is a directory, should be a Go file", file)
|
||||
}
|
||||
|
||||
// 2.2 所有 *.go 必须在同一个目录内
|
||||
dir1, _ := filepath.Split(file)
|
||||
if dir1 == "" {
|
||||
dir1 = "./"
|
||||
}
|
||||
// 2.2 所有 *.go 必须在同一个目录内
|
||||
dir1, _ := filepath.Split(file)
|
||||
if dir1 == "" {
|
||||
dir1 = "./"
|
||||
}
|
||||
|
||||
if dir == "" {
|
||||
dir = dir1
|
||||
} else if dir != dir1 {
|
||||
return fmt.Errorf("named files must all be in one directory: have %s and %s", dir, dir1)
|
||||
}
|
||||
}
|
||||
if dir == "" {
|
||||
dir = dir1
|
||||
} else if dir != dir1 {
|
||||
return fmt.Errorf("named files must all be in one directory: have %s and %s", dir, dir1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDirFromImportPaths return the import path's real abs directory
|
||||
@ -414,90 +414,90 @@ func goFilesPackage(gofiles []string) error {
|
||||
// 该函数接收到的只有 dir 或 import path,file 在上一步已被排除
|
||||
// 只考虑 go modules 的情况
|
||||
func getDirFromImportPaths(patterns []string) (string, error) {
|
||||
// no import path, pattern = current wd
|
||||
if len(patterns) == 0 {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fail to parse import path: %w", err)
|
||||
}
|
||||
return wd, nil
|
||||
}
|
||||
// no import path, pattern = current wd
|
||||
if len(patterns) == 0 {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fail to parse import path: %w", err)
|
||||
}
|
||||
return wd, nil
|
||||
}
|
||||
|
||||
// 为了简化插桩的逻辑,goc 对 import path 要求必须都在同一个目录
|
||||
// 所以干脆只允许一个 pattern 得了 -_-
|
||||
// 对于 goc build/run 来说本身就是只能在一个目录内
|
||||
// 对于 goc install 来讲,这个行为就和 go install 不同,不过多 import path 较少见 >_<,先忽略
|
||||
if len(patterns) > 1 {
|
||||
return "", fmt.Errorf("goc only support one import path now")
|
||||
}
|
||||
// 为了简化插桩的逻辑,goc 对 import path 要求必须都在同一个目录
|
||||
// 所以干脆只允许一个 pattern 得了 -_-
|
||||
// 对于 goc build/run 来说本身就是只能在一个目录内
|
||||
// 对于 goc install 来讲,这个行为就和 go install 不同,不过多 import path 较少见 >_<,先忽略
|
||||
if len(patterns) > 1 {
|
||||
return "", fmt.Errorf("goc only support one import path now")
|
||||
}
|
||||
|
||||
pattern := patterns[0]
|
||||
switch {
|
||||
// case isLocalImport(pattern) || filepath.IsAbs(pattern):
|
||||
// dir1, err := filepath.Abs(pattern)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// if _, err := os.Stat(dir1); err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// return dir1, nil
|
||||
pattern := patterns[0]
|
||||
switch {
|
||||
// case isLocalImport(pattern) || filepath.IsAbs(pattern):
|
||||
// dir1, err := filepath.Abs(pattern)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// if _, err := os.Stat(dir1); err != nil {
|
||||
// return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
// }
|
||||
// return dir1, nil
|
||||
|
||||
case strings.Contains(pattern, "..."):
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := filepath.Split(pattern[:i])
|
||||
dir, _ = filepath.Abs(dir)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
return dir, nil
|
||||
case strings.Contains(pattern, "..."):
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := filepath.Split(pattern[:i])
|
||||
dir, _ = filepath.Abs(dir)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
return dir, nil
|
||||
|
||||
case strings.IndexByte(pattern, '@') > 0:
|
||||
return "", fmt.Errorf("import path with @ version query is not supported in goc")
|
||||
case strings.IndexByte(pattern, '@') > 0:
|
||||
return "", fmt.Errorf("import path with @ version query is not supported in goc")
|
||||
|
||||
case isMetaPackage(pattern):
|
||||
return "", fmt.Errorf("`std`, `cmd`, `all` import path is not supported by goc")
|
||||
case isMetaPackage(pattern):
|
||||
return "", fmt.Errorf("`std`, `cmd`, `all` import path is not supported by goc")
|
||||
|
||||
default: // 到这一步认为 pattern 是相对路径或者绝对路径
|
||||
dir1, err := filepath.Abs(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
if _, err := os.Stat(dir1); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
default: // 到这一步认为 pattern 是相对路径或者绝对路径
|
||||
dir1, err := filepath.Abs(pattern)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
if _, err := os.Stat(dir1); err != nil {
|
||||
return "", fmt.Errorf("error (%w) get directory from the import path: %v", err, pattern)
|
||||
}
|
||||
|
||||
return dir1, nil
|
||||
}
|
||||
return dir1, nil
|
||||
}
|
||||
}
|
||||
|
||||
// isLocalImport reports whether the import path is
|
||||
// a local import path, like ".", "..", "./foo", or "../foo"
|
||||
func isLocalImport(path string) bool {
|
||||
return path == "." || path == ".." ||
|
||||
strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
|
||||
return path == "." || path == ".." ||
|
||||
strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
|
||||
}
|
||||
|
||||
// isMetaPackage checks if the name is a reserved package name
|
||||
func isMetaPackage(name string) bool {
|
||||
return name == "std" || name == "cmd" || name == "all"
|
||||
return name == "std" || name == "cmd" || name == "all"
|
||||
}
|
||||
|
||||
// find direct path of current project which contains go.mod
|
||||
func findModuleRoot(dir string) string {
|
||||
dir = filepath.Clean(dir)
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
// look for enclosing go.mod
|
||||
for {
|
||||
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
|
||||
return dir
|
||||
}
|
||||
d := filepath.Dir(dir)
|
||||
if d == dir {
|
||||
break
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
// look for enclosing go.mod
|
||||
for {
|
||||
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
|
||||
return dir
|
||||
}
|
||||
d := filepath.Dir(dir)
|
||||
if d == dir {
|
||||
break
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
|
||||
return ""
|
||||
return ""
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
@ -60,3 +62,9 @@ func WithDebug(enable bool) gocOption {
|
||||
b.Debug = enable
|
||||
}
|
||||
}
|
||||
|
||||
func WithExtra(extra string) gocOption {
|
||||
return func(b *Build) {
|
||||
b.Extra = strings.TrimSpace(extra)
|
||||
}
|
||||
}
|
||||
|
@ -14,141 +14,143 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
// readProjectMetaInfo reads all meta informations of the corresponding project
|
||||
func (b *Build) readProjectMetaInfo() {
|
||||
// get gopath & gobin
|
||||
b.GOPATH = b.readGOPATH()
|
||||
b.GOBIN = b.readGOBIN()
|
||||
// 获取 [packages] 及其依赖的 package list
|
||||
pkgs := b.listPackages(b.CurWd)
|
||||
func (b *Build) readProjectMetaInfo(noTemp ...bool) {
|
||||
// get gopath & gobin
|
||||
b.GOPATH = b.readGOPATH()
|
||||
b.GOBIN = b.readGOBIN()
|
||||
// 获取 [packages] 及其依赖的 package list
|
||||
pkgs := b.listPackages(b.CurWd)
|
||||
|
||||
// get mod info
|
||||
for _, pkg := range pkgs {
|
||||
// check if go modules is enabled
|
||||
if pkg.Module == nil {
|
||||
log.Fatalf("Go module is not enabled, please set GO111MODULE=auto or on")
|
||||
}
|
||||
// 工程根目录
|
||||
b.CurModProjectDir = pkg.Module.Dir
|
||||
b.ImportPath = pkg.Module.Path
|
||||
// get mod info
|
||||
for _, pkg := range pkgs {
|
||||
// check if go modules is enabled
|
||||
if pkg.Module == nil {
|
||||
log.Fatalf("Go module is not enabled, please set GO111MODULE=auto or on")
|
||||
}
|
||||
// 工程根目录
|
||||
b.CurModProjectDir = pkg.Module.Dir
|
||||
b.ImportPath = pkg.Module.Path
|
||||
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// 如果当前目录不是工程根目录,那再次 go list 一次,获取整个工程的包信息
|
||||
if b.CurWd != b.CurModProjectDir {
|
||||
b.Pkgs = b.listPackages(b.CurModProjectDir)
|
||||
} else {
|
||||
b.Pkgs = pkgs
|
||||
}
|
||||
// 如果当前目录不是工程根目录,那再次 go list 一次,获取整个工程的包信息
|
||||
if b.CurWd != b.CurModProjectDir {
|
||||
b.Pkgs = b.listPackages(b.CurModProjectDir)
|
||||
} else {
|
||||
b.Pkgs = pkgs
|
||||
}
|
||||
|
||||
// check if project is in vendor mod
|
||||
b.checkIfVendorMod()
|
||||
// check if project is in vendor mod
|
||||
b.checkIfVendorMod()
|
||||
|
||||
// get tmp folder name
|
||||
b.TmpModProjectDir = filepath.Join(os.TempDir(), TmpFolderName(b.CurModProjectDir))
|
||||
// get working dir in the corresponding tmp dir
|
||||
b.TmpWd = filepath.Join(b.TmpModProjectDir, b.CurWd[len(b.CurModProjectDir):])
|
||||
// get GlobalCoverVarImportPath
|
||||
b.GlobalCoverVarImportPath = path.Join(b.ImportPath, TmpFolderName(b.CurModProjectDir))
|
||||
log.Donef("project meta information parsed")
|
||||
// get tmp folder name
|
||||
b.TmpModProjectDir = filepath.Join(os.TempDir(), TmpFolderName(b.CurModProjectDir))
|
||||
if len(noTemp) != 0 && noTemp[0] {
|
||||
b.TmpModProjectDir = b.CurModProjectDir
|
||||
}
|
||||
// get working dir in the corresponding tmp dir
|
||||
b.TmpWd = filepath.Join(b.TmpModProjectDir, b.CurWd[len(b.CurModProjectDir):])
|
||||
// get GlobalCoverVarImportPath
|
||||
b.GlobalCoverVarImportPath = path.Join(b.ImportPath, TmpFolderName(b.CurModProjectDir))
|
||||
log.Donef("project meta information parsed")
|
||||
}
|
||||
|
||||
// displayProjectMetaInfo prints basic infomation of this project to stdout
|
||||
func (b *Build) displayProjectMetaInfo() {
|
||||
log.Infof("GOPATH: %v", b.GOPATH)
|
||||
log.Infof("GOBIN: %v", b.GOBIN)
|
||||
log.Infof("Project Directory: %v", b.CurModProjectDir)
|
||||
log.Infof("GOC_REGISTER_EXTRA from env: %v", os.Getenv("GOC_REGISTER_EXTRA"))
|
||||
log.Infof("Temporary Project Directory: %v", b.TmpModProjectDir)
|
||||
if b.IsVendorMod {
|
||||
log.Infof("Project in vendor mod")
|
||||
}
|
||||
log.Infof("")
|
||||
log.Infof("GOPATH: %v", b.GOPATH)
|
||||
log.Infof("GOBIN: %v", b.GOBIN)
|
||||
log.Infof("Project Directory: %v", b.CurModProjectDir)
|
||||
log.Infof("Temporary Project Directory: %v", b.TmpModProjectDir)
|
||||
if b.IsVendorMod {
|
||||
log.Infof("Project in vendor mod")
|
||||
}
|
||||
log.Infof("")
|
||||
}
|
||||
|
||||
// readGOPATH reads GOPATH use go env GOPATH command
|
||||
func (b *Build) readGOPATH() string {
|
||||
out, err := exec.Command("go", "env", "GOPATH").Output()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to read GOPATH: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
out, err := exec.Command("go", "env", "GOPATH").Output()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to read GOPATH: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// readGOBIN reads GOBIN use go env GOBIN command
|
||||
func (b *Build) readGOBIN() string {
|
||||
out, err := exec.Command("go", "env", "GOBIN").Output()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to read GOBIN: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
out, err := exec.Command("go", "env", "GOBIN").Output()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to read GOBIN: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// listPackages list all packages under specific via go list command.
|
||||
func (b *Build) listPackages(dir string) map[string]*Package {
|
||||
listArgs := []string{"list", "-json"}
|
||||
if goflags.BuildTags != "" {
|
||||
listArgs = append(listArgs, "-tags", goflags.BuildTags)
|
||||
}
|
||||
listArgs = append(listArgs, "./...")
|
||||
listArgs := []string{"list", "-json"}
|
||||
if goflags.BuildTags != "" {
|
||||
listArgs = append(listArgs, "-tags", goflags.BuildTags)
|
||||
}
|
||||
listArgs = append(listArgs, "./...")
|
||||
|
||||
cmd := exec.Command("go", listArgs...)
|
||||
cmd.Dir = dir
|
||||
cmd := exec.Command("go", listArgs...)
|
||||
cmd.Dir = dir
|
||||
|
||||
var errBuf bytes.Buffer
|
||||
cmd.Stderr = &errBuf
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatalf("execute go list -json failed, err: %v, stdout: %v, stderr: %v", err, string(out), errBuf.String())
|
||||
}
|
||||
// 有些时候 go 命令会打印一些信息到 stderr,但其实命令整体是成功运行了
|
||||
if errBuf.String() != "" {
|
||||
log.Errorf("%v", errBuf.String())
|
||||
}
|
||||
var errBuf bytes.Buffer
|
||||
cmd.Stderr = &errBuf
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatalf("execute go list -json failed, err: %v, stdout: %v, stderr: %v", err, string(out), errBuf.String())
|
||||
}
|
||||
// 有些时候 go 命令会打印一些信息到 stderr,但其实命令整体是成功运行了
|
||||
if errBuf.String() != "" {
|
||||
log.Errorf("%v", errBuf.String())
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewBuffer(out))
|
||||
pkgs := make(map[string]*Package)
|
||||
dec := json.NewDecoder(bytes.NewBuffer(out))
|
||||
pkgs := make(map[string]*Package)
|
||||
|
||||
for {
|
||||
var pkg Package
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Fatalf("reading go list output error: %v", err)
|
||||
}
|
||||
if pkg.Error != nil {
|
||||
log.Fatalf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
|
||||
}
|
||||
for {
|
||||
var pkg Package
|
||||
if err := dec.Decode(&pkg); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Fatalf("reading go list output error: %v", err)
|
||||
}
|
||||
if pkg.Error != nil {
|
||||
log.Fatalf("list package %s failed with output: %v", pkg.ImportPath, pkg.Error)
|
||||
}
|
||||
|
||||
pkgs[pkg.ImportPath] = &pkg
|
||||
}
|
||||
pkgs[pkg.ImportPath] = &pkg
|
||||
}
|
||||
|
||||
return pkgs
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func (b *Build) checkIfVendorMod() {
|
||||
if b.IsVendorMod == true {
|
||||
return
|
||||
}
|
||||
if b.IsVendorMod == true {
|
||||
return
|
||||
}
|
||||
|
||||
vendorDir := filepath.Join(b.CurModProjectDir, "vendor")
|
||||
if _, err := os.Stat(vendorDir); err != nil {
|
||||
b.IsVendorMod = false
|
||||
}
|
||||
vendorDir := filepath.Join(b.CurModProjectDir, "vendor")
|
||||
if _, err := os.Stat(vendorDir); err != nil {
|
||||
b.IsVendorMod = false
|
||||
}
|
||||
|
||||
b.IsVendorMod = true
|
||||
b.IsVendorMod = true
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/build/internal/tool"
|
||||
"github.com/RickLeee/goc/v2/pkg/build/internal/websocket"
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/build/internal/tool"
|
||||
"github.com/ar0c/goc/v2/pkg/build/internal/websocket"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
// Inject injects cover variables for all the .go files in the target directory
|
||||
@ -195,7 +195,7 @@ func (b *Build) injectGocAgent(where string, covers []*PackageCover) {
|
||||
cmd := exec.Command("git", "rev-parse", "--short=8", "HEAD")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Errorf("git describe Error: %v", err)
|
||||
log.Errorf("git rev-parse Error: %v", err)
|
||||
} else {
|
||||
commitID = strings.TrimRight(string(output), "\n")
|
||||
}
|
||||
@ -205,12 +205,7 @@ func (b *Build) injectGocAgent(where string, covers []*PackageCover) {
|
||||
if err != nil {
|
||||
log.Errorf("get git branch Error: %v", err)
|
||||
} else {
|
||||
log.Infof("[goc][info] raw branch: %v ", br)
|
||||
branch = strings.Replace(string(br), "\n", "", -1)
|
||||
branch = strings.TrimLeft(branch, "heads/")
|
||||
branch = strings.Trim(branch, " ")
|
||||
branch = strings.Replace(branch, "/", "-", -1)
|
||||
branch = strings.Replace(branch, "_", "-", -1)
|
||||
branch = fmtBranch(br)
|
||||
}
|
||||
log.Infof("[goc][info] branch: %v --- commitID: %v", branch, commitID)
|
||||
tmplData := struct {
|
||||
@ -221,6 +216,7 @@ func (b *Build) injectGocAgent(where string, covers []*PackageCover) {
|
||||
Mode string
|
||||
CommitID string
|
||||
Branch string
|
||||
Extra string
|
||||
}{
|
||||
Covers: covers,
|
||||
GlobalCoverVarImportPath: b.GlobalCoverVarImportPath,
|
||||
@ -229,6 +225,7 @@ func (b *Build) injectGocAgent(where string, covers []*PackageCover) {
|
||||
Mode: _coverMode,
|
||||
Branch: branch,
|
||||
CommitID: commitID,
|
||||
Extra: b.Extra,
|
||||
}
|
||||
|
||||
if err := coverMainTmpl.Execute(f2, tmplData); err != nil {
|
||||
@ -317,3 +314,11 @@ func UploadCoverChangeEvent_%v(name string, pos []uint32, i int, stmts uint16) {
|
||||
log.Fatalf("fail to write to global cover definition file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func fmtBranch(br []byte) (branch string) {
|
||||
branch = strings.Replace(string(br), "\n", "", -1)
|
||||
branch = strings.TrimLeft(branch, "heads/")
|
||||
branch = strings.Trim(branch, " ")
|
||||
branch = strings.Replace(branch, "/", "-", -1)
|
||||
return
|
||||
}
|
||||
|
@ -14,14 +14,14 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
func NewInstall(opts ...gocOption) *Build {
|
||||
return NewBuild(opts...)
|
||||
return NewBuild(opts...)
|
||||
}
|
||||
|
||||
// Install starts go install
|
||||
@ -30,51 +30,51 @@ func NewInstall(opts ...gocOption) *Build {
|
||||
// 2. inject cover variables and functions into the project,
|
||||
// 3. install the project in temp.
|
||||
func (b *Build) Install() {
|
||||
// 1. 拷贝至临时目录
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
// 1. 拷贝至临时目录
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
|
||||
log.Donef("project copied to temporary directory")
|
||||
log.Donef("project copied to temporary directory")
|
||||
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
|
||||
// 4. install in the temp project
|
||||
b.doInstallInTemp()
|
||||
// 4. install in the temp project
|
||||
b.doInstallInTemp()
|
||||
}
|
||||
|
||||
func (b *Build) doInstallInTemp() {
|
||||
log.StartWait("installing the injected project")
|
||||
log.StartWait("installing the injected project")
|
||||
|
||||
goflags := b.Goflags
|
||||
goflags := b.Goflags
|
||||
|
||||
pacakges := b.Packages
|
||||
pacakges := b.Packages
|
||||
|
||||
goflags = append(goflags, pacakges...)
|
||||
goflags = append(goflags, pacakges...)
|
||||
|
||||
args := []string{"install"}
|
||||
args = append(args, goflags...)
|
||||
// go 命令行由 go install [build flags] [packages] 组成
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
args := []string{"install"}
|
||||
args = append(args, goflags...)
|
||||
// go 命令行由 go install [build flags] [packages] 组成
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
log.Infof("go install cmd is: %v, in path [%v]", cmd.Args, cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go install: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("fail to execute go install: %v", err)
|
||||
}
|
||||
log.Infof("go install cmd is: %v, in path [%v]", cmd.Args, cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("fail to execute go install: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("fail to execute go install: %v", err)
|
||||
}
|
||||
|
||||
// done
|
||||
log.StopWait()
|
||||
log.Donef("go install done")
|
||||
// done
|
||||
log.StopWait()
|
||||
log.Donef("go install done")
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,14 +14,14 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"embed"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"embed"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
//go:embed websocket.tar
|
||||
@ -32,48 +32,48 @@ var depTarFile embed.FS
|
||||
// 从 embed 文件系统中解压 websocket.tar 文件,并依次写入临时工程中,作为一个单独的包存在。
|
||||
// gorrila/websocket 是一个无第三方依赖的库,因此其位置可以随处移动,而不影响自身的编译。
|
||||
func AddCustomWebsocketDep(customWebsocketPath string) {
|
||||
data, err := depTarFile.ReadFile("websocket.tar")
|
||||
if err != nil {
|
||||
log.Fatalf("cannot find the websocket.tar in the embed file: %v", err)
|
||||
}
|
||||
data, err := depTarFile.ReadFile("websocket.tar")
|
||||
if err != nil {
|
||||
log.Fatalf("cannot find the websocket.tar in the embed file: %v", err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
tr := tar.NewReader(buf)
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("cannot untar the websocket.tar: %v", err)
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
tr := tar.NewReader(buf)
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("cannot untar the websocket.tar: %v", err)
|
||||
}
|
||||
|
||||
fpath := filepath.Join(customWebsocketPath, hdr.Name)
|
||||
if hdr.FileInfo().IsDir() {
|
||||
// 处理目录
|
||||
err := os.MkdirAll(fpath, hdr.FileInfo().Mode())
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 处理文件
|
||||
fdir := filepath.Dir(fpath)
|
||||
err := os.MkdirAll(fdir, hdr.FileInfo().Mode())
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
fpath := filepath.Join(customWebsocketPath, hdr.Name)
|
||||
if hdr.FileInfo().IsDir() {
|
||||
// 处理目录
|
||||
err := os.MkdirAll(fpath, hdr.FileInfo().Mode())
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 处理文件
|
||||
fdir := filepath.Dir(fpath)
|
||||
err := os.MkdirAll(fdir, hdr.FileInfo().Mode())
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, hdr.FileInfo().Mode())
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, hdr.FileInfo().Mode())
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(f, tr)
|
||||
_, err = io.Copy(f, tr)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("fail to untar the websocket.tar: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
126
pkg/build/run.go
126
pkg/build/run.go
@ -14,33 +14,33 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/RickLeee/goc/v2/pkg/server"
|
||||
"github.com/RickLeee/goc/v2/pkg/server/store"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/server"
|
||||
"github.com/ar0c/goc/v2/pkg/server/store"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewRun(opts ...gocOption) *Build {
|
||||
b := &Build{}
|
||||
b := &Build{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(b)
|
||||
}
|
||||
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
b.runCmdArgsParse()
|
||||
// 2. 解析 go 包位置
|
||||
// b.getPackagesDir()
|
||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||
b.readProjectMetaInfo()
|
||||
// 4. 展示元信息
|
||||
b.displayProjectMetaInfo()
|
||||
// 1. 解析 goc 命令行和 go 命令行
|
||||
b.runCmdArgsParse()
|
||||
// 2. 解析 go 包位置
|
||||
// b.getPackagesDir()
|
||||
// 3. 读取工程元信息:go.mod, pkgs list ...
|
||||
b.readProjectMetaInfo()
|
||||
// 4. 展示元信息
|
||||
b.displayProjectMetaInfo()
|
||||
|
||||
return b
|
||||
return b
|
||||
}
|
||||
|
||||
// Run starts go run
|
||||
@ -49,58 +49,58 @@ func NewRun(opts ...gocOption) *Build {
|
||||
// 2. inject cover variables and functions into the project,
|
||||
// 3. run the project in temp.
|
||||
func (b *Build) Run() {
|
||||
// 1. 拷贝至临时目录
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
// 1. 拷贝至临时目录
|
||||
b.copyProjectToTmp()
|
||||
defer b.clean()
|
||||
|
||||
log.Donef("project copied to temporary directory")
|
||||
log.Donef("project copied to temporary directory")
|
||||
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
// 2. update go.mod file if needed
|
||||
b.updateGoModFile()
|
||||
// 3. inject cover vars
|
||||
b.Inject()
|
||||
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
if b.IsVendorMod && b.IsModEdit {
|
||||
b.reVendor()
|
||||
}
|
||||
|
||||
// 4. run in the temp project
|
||||
go func() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, os.Interrupt)
|
||||
<-ch
|
||||
b.clean()
|
||||
}()
|
||||
b.doRunInTemp()
|
||||
// 4. run in the temp project
|
||||
go func() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, os.Interrupt)
|
||||
<-ch
|
||||
b.clean()
|
||||
}()
|
||||
b.doRunInTemp()
|
||||
}
|
||||
|
||||
func (b *Build) doRunInTemp() {
|
||||
log.Infof("running the injected project")
|
||||
log.Infof("running the injected project")
|
||||
|
||||
s := store.NewFakeStore()
|
||||
go func() {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
err := server.RunGocServerUntilExit(b.Host, s)
|
||||
if err != nil {
|
||||
log.Fatalf("goc server fail to run: %v", err)
|
||||
}
|
||||
}()
|
||||
s := store.NewFakeStore()
|
||||
go func() {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
err := server.RunGocServerUntilExit(b.Host, s)
|
||||
if err != nil {
|
||||
log.Fatalf("goc server fail to run: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
args := []string{"run"}
|
||||
args = append(args, b.GoArgs...)
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
args := []string{"run"}
|
||||
args = append(args, b.GoArgs...)
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Dir = b.TmpWd
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
log.Infof("go run cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Errorf("fail to execute go run: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Errorf("fail to execute go run: %v", err)
|
||||
}
|
||||
log.Infof("go run cmd is: %v, in path [%v]", nicePrintArgs(cmd.Args), cmd.Dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Errorf("fail to execute go run: %v", err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Errorf("fail to execute go run: %v", err)
|
||||
}
|
||||
|
||||
// done
|
||||
log.Donef("go run done")
|
||||
// done
|
||||
log.Donef("go run done")
|
||||
}
|
||||
|
@ -14,79 +14,79 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/tongjingran/copy"
|
||||
"golang.org/x/mod/modfile"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/tongjingran/copy"
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// copyProjectToTmp copies project files to the temporary directory
|
||||
//
|
||||
// It will ignore .git and irregular files, only copy source(text) files
|
||||
func (b *Build) copyProjectToTmp() {
|
||||
curProject := b.CurModProjectDir
|
||||
tmpProject := b.TmpModProjectDir
|
||||
curProject := b.CurModProjectDir
|
||||
tmpProject := b.TmpModProjectDir
|
||||
|
||||
if _, err := os.Stat(tmpProject); !os.IsNotExist(err) {
|
||||
log.Infof("find previous temporary directory, delete")
|
||||
err := os.RemoveAll(tmpProject)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to remove preivous temporary directory: %v", err)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(tmpProject); !os.IsNotExist(err) {
|
||||
log.Infof("find previous temporary directory, delete")
|
||||
err := os.RemoveAll(tmpProject)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to remove preivous temporary directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.StartWait("coping project")
|
||||
err := os.MkdirAll(tmpProject, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to create temporary directory: %v", err)
|
||||
}
|
||||
log.StartWait("coping project")
|
||||
err := os.MkdirAll(tmpProject, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to create temporary directory: %v", err)
|
||||
}
|
||||
|
||||
// copy
|
||||
if err := copy.Copy(curProject, tmpProject, copy.Options{Skip: skipCopy}); err != nil {
|
||||
log.Fatalf("fail to copy the folder from %v to %v, the err: %v", curProject, tmpProject, err)
|
||||
}
|
||||
// copy
|
||||
if err := copy.Copy(curProject, tmpProject, copy.Options{Skip: skipCopy}); err != nil {
|
||||
log.Fatalf("fail to copy the folder from %v to %v, the err: %v", curProject, tmpProject, err)
|
||||
}
|
||||
|
||||
log.StopWait()
|
||||
log.StopWait()
|
||||
}
|
||||
|
||||
// TmpFolderName generates a directory name according to the path
|
||||
func TmpFolderName(path string) string {
|
||||
sum := sha256.Sum256([]byte(path))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
sum := sha256.Sum256([]byte(path))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
|
||||
return "gocbuild" + h
|
||||
return "gocbuild" + h
|
||||
}
|
||||
|
||||
// skipCopy skip copy .git dir and irregular files
|
||||
func skipCopy(src string, info os.FileInfo) (bool, error) {
|
||||
irregularModeType := os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
|
||||
if strings.HasSuffix(src, "/.git") {
|
||||
log.Debugf("skip .git dir [%s]", src)
|
||||
return true, nil
|
||||
}
|
||||
if info.Mode()&irregularModeType != 0 {
|
||||
log.Debugf("skip file [%s], the file mode is [%s]", src, info.Mode().String())
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
irregularModeType := os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
|
||||
if strings.HasSuffix(src, "/.git") {
|
||||
log.Debugf("skip .git dir [%s]", src)
|
||||
return true, nil
|
||||
}
|
||||
if info.Mode()&irregularModeType != 0 {
|
||||
log.Debugf("skip file [%s], the file mode is [%s]", src, info.Mode().String())
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// clean clears the temporary project
|
||||
func (b *Build) clean() {
|
||||
if !b.Debug {
|
||||
if err := os.RemoveAll(b.TmpModProjectDir); err != nil {
|
||||
log.Fatalf("fail to delete the temporary project: %v", err)
|
||||
}
|
||||
log.Donef("delete the temporary project")
|
||||
} else {
|
||||
log.Debugf("--debug is enabled, keep the temporary project")
|
||||
}
|
||||
if !b.Debug {
|
||||
if err := os.RemoveAll(b.TmpModProjectDir); err != nil {
|
||||
log.Fatalf("fail to delete the temporary project: %v", err)
|
||||
}
|
||||
log.Donef("delete the temporary project")
|
||||
} else {
|
||||
log.Debugf("--debug is enabled, keep the temporary project")
|
||||
}
|
||||
}
|
||||
|
||||
// updateGoModFile rewrites the go.mod file in the temporary directory,
|
||||
@ -102,50 +102,50 @@ func (b *Build) clean() {
|
||||
// after the project is copied to temporary directory, it should be rewritten as
|
||||
// 'replace github.com/qiniu/bar => /path/to/aa/bb/home/foo/bar'
|
||||
func (b *Build) updateGoModFile() (updateFlag bool, newModFile []byte) {
|
||||
tempModfile := filepath.Join(b.TmpModProjectDir, "go.mod")
|
||||
buf, err := ioutil.ReadFile(tempModfile)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot find go.mod file in temporary directory: %v", err)
|
||||
}
|
||||
oriGoModFile, err := modfile.Parse(tempModfile, buf, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot parse go.mod: %v", err)
|
||||
}
|
||||
tempModfile := filepath.Join(b.TmpModProjectDir, "go.mod")
|
||||
buf, err := ioutil.ReadFile(tempModfile)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot find go.mod file in temporary directory: %v", err)
|
||||
}
|
||||
oriGoModFile, err := modfile.Parse(tempModfile, buf, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot parse go.mod: %v", err)
|
||||
}
|
||||
|
||||
updateFlag = false
|
||||
for index := range oriGoModFile.Replace {
|
||||
replace := oriGoModFile.Replace[index]
|
||||
oldPath := replace.Old.Path
|
||||
oldVersion := replace.Old.Version
|
||||
newPath := replace.New.Path
|
||||
newVersion := replace.New.Version
|
||||
// replace to a local filesystem does not have a version
|
||||
// absolute path no need to rewrite
|
||||
if newVersion == "" && !filepath.IsAbs(newPath) {
|
||||
var absPath string
|
||||
fullPath := filepath.Join(b.CurModProjectDir, newPath)
|
||||
absPath, _ = filepath.Abs(fullPath)
|
||||
// DropReplace & AddReplace will not return error
|
||||
// so no need to check the error
|
||||
_ = oriGoModFile.DropReplace(oldPath, oldVersion)
|
||||
_ = oriGoModFile.AddReplace(oldPath, oldVersion, absPath, newVersion)
|
||||
updateFlag = true
|
||||
}
|
||||
}
|
||||
oriGoModFile.Cleanup()
|
||||
// Format will not return error, so ignore the returned error
|
||||
// func (f *File) Format() ([]byte, error) {
|
||||
// return Format(f.Syntax), nil
|
||||
// }
|
||||
newModFile, _ = oriGoModFile.Format()
|
||||
updateFlag = false
|
||||
for index := range oriGoModFile.Replace {
|
||||
replace := oriGoModFile.Replace[index]
|
||||
oldPath := replace.Old.Path
|
||||
oldVersion := replace.Old.Version
|
||||
newPath := replace.New.Path
|
||||
newVersion := replace.New.Version
|
||||
// replace to a local filesystem does not have a version
|
||||
// absolute path no need to rewrite
|
||||
if newVersion == "" && !filepath.IsAbs(newPath) {
|
||||
var absPath string
|
||||
fullPath := filepath.Join(b.CurModProjectDir, newPath)
|
||||
absPath, _ = filepath.Abs(fullPath)
|
||||
// DropReplace & AddReplace will not return error
|
||||
// so no need to check the error
|
||||
_ = oriGoModFile.DropReplace(oldPath, oldVersion)
|
||||
_ = oriGoModFile.AddReplace(oldPath, oldVersion, absPath, newVersion)
|
||||
updateFlag = true
|
||||
}
|
||||
}
|
||||
oriGoModFile.Cleanup()
|
||||
// Format will not return error, so ignore the returned error
|
||||
// func (f *File) Format() ([]byte, error) {
|
||||
// return Format(f.Syntax), nil
|
||||
// }
|
||||
newModFile, _ = oriGoModFile.Format()
|
||||
|
||||
if updateFlag {
|
||||
log.Infof("go.mod needs rewrite")
|
||||
err := os.WriteFile(tempModfile, newModFile, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to update go.mod: %v", err)
|
||||
}
|
||||
b.IsModEdit = true
|
||||
}
|
||||
return
|
||||
if updateFlag {
|
||||
log.Infof("go.mod needs rewrite")
|
||||
err := os.WriteFile(tempModfile, newModFile, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to update go.mod: %v", err)
|
||||
}
|
||||
b.IsModEdit = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -14,83 +14,83 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/client/rest"
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/ar0c/goc/v2/pkg/client/rest"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const (
|
||||
DISCONNECT = 1 << iota
|
||||
RPCCONNECT = 1 << iota
|
||||
WATCHCONNECT = 1 << iota
|
||||
DISCONNECT = 1 << iota
|
||||
RPCCONNECT = 1 << iota
|
||||
WATCHCONNECT = 1 << iota
|
||||
)
|
||||
|
||||
func ListAgents(host string, ids []string, wide, isJson bool) {
|
||||
gocClient := rest.NewV2Client(host)
|
||||
gocClient := rest.NewV2Client(host)
|
||||
|
||||
agents, err := gocClient.Agent().Get(ids)
|
||||
agents, err := gocClient.Agent().Get(ids)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("cannot get agent list from goc server: %v", err)
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
if isJson {
|
||||
goto asJson
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("cannot get agent list from goc server: %v", err)
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
if isJson {
|
||||
goto asJson
|
||||
}
|
||||
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding(" ") // pad with 3 blank spaces
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetReflowDuringAutoWrap(false)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAutoWrapText(false)
|
||||
if wide {
|
||||
table.SetHeader([]string{"ID", "STATUS", "REMOTEIP", "HOSTNAME", "PID", "CMD", "EXTRA"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
} else {
|
||||
table.SetHeader([]string{"ID", "STATUS", "REMOTEIP", "CMD"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
}
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding(" ") // pad with 3 blank spaces
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetReflowDuringAutoWrap(false)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAutoWrapText(false)
|
||||
if wide {
|
||||
table.SetHeader([]string{"ID", "STATUS", "REMOTEIP", "HOSTNAME", "PID", "CMD", "EXTRA"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
} else {
|
||||
table.SetHeader([]string{"ID", "STATUS", "REMOTEIP", "CMD"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
}
|
||||
asJson:
|
||||
for _, agent := range agents {
|
||||
var status string
|
||||
if agent.Status == DISCONNECT {
|
||||
status = "DISCONNECT"
|
||||
} else if agent.Status&(RPCCONNECT|WATCHCONNECT) > 0 {
|
||||
status = "CONNECT"
|
||||
}
|
||||
agent.StatusStr = status
|
||||
if !isJson {
|
||||
if wide {
|
||||
table.Append([]string{agent.Id, status, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine, agent.Extra})
|
||||
} else {
|
||||
preLen := len(agent.Id) + len(agent.RemoteIP) + 9
|
||||
table.Append([]string{agent.Id, status, agent.RemoteIP, getSimpleCmdLine(preLen, agent.CmdLine)})
|
||||
}
|
||||
}
|
||||
}
|
||||
if !isJson {
|
||||
table.Render()
|
||||
} else {
|
||||
b, _ := json.Marshal(agents)
|
||||
fmt.Fprint(os.Stdout, string(b))
|
||||
}
|
||||
for _, agent := range agents {
|
||||
var status string
|
||||
if agent.Status == DISCONNECT {
|
||||
status = "DISCONNECT"
|
||||
} else if agent.Status&(RPCCONNECT|WATCHCONNECT) > 0 {
|
||||
status = "CONNECT"
|
||||
}
|
||||
agent.StatusStr = status
|
||||
if !isJson {
|
||||
if wide {
|
||||
table.Append([]string{agent.Id, status, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine, agent.Extra})
|
||||
} else {
|
||||
preLen := len(agent.Id) + len(agent.RemoteIP) + 9
|
||||
table.Append([]string{agent.Id, status, agent.RemoteIP, getSimpleCmdLine(preLen, agent.CmdLine)})
|
||||
}
|
||||
}
|
||||
}
|
||||
if !isJson {
|
||||
table.Render()
|
||||
} else {
|
||||
b, _ := json.Marshal(agents)
|
||||
fmt.Fprint(os.Stdout, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteAgents(host string, ids []string) {
|
||||
gocClient := rest.NewV2Client(host)
|
||||
gocClient := rest.NewV2Client(host)
|
||||
|
||||
err := gocClient.Agent().Delete(ids)
|
||||
err := gocClient.Agent().Delete(ids)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("cannot delete agents from goc server: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("cannot delete agents from goc server: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -14,198 +14,198 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/term"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
// Action provides methods to contact with the covered agent under test
|
||||
type Action interface {
|
||||
ListAgents(bool)
|
||||
Profile(string)
|
||||
ListAgents(bool)
|
||||
Profile(string)
|
||||
}
|
||||
|
||||
const (
|
||||
// CoverAgentsListAPI list all the registered agents
|
||||
CoverAgentsListAPI = "/v2/rpcagents"
|
||||
//CoverProfileAPI is provided by the covered service to get profiles
|
||||
CoverProfileAPI = "/v2/cover/profile"
|
||||
// CoverAgentsListAPI list all the registered agents
|
||||
CoverAgentsListAPI = "/v2/rpcagents"
|
||||
//CoverProfileAPI is provided by the covered service to get profiles
|
||||
CoverProfileAPI = "/v2/cover/profile"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
Host string
|
||||
client *http.Client
|
||||
Host string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// gocListAgents response of the list request
|
||||
type gocListAgents struct {
|
||||
Items []gocCoveredAgent `json:"items"`
|
||||
Items []gocCoveredAgent `json:"items"`
|
||||
}
|
||||
|
||||
// gocCoveredAgent represents a covered client
|
||||
type gocCoveredAgent struct {
|
||||
Id string `json:"id"`
|
||||
RemoteIP string `json:"remoteip"`
|
||||
Hostname string `json:"hostname"`
|
||||
CmdLine string `json:"cmdline"`
|
||||
Pid string `json:"pid"`
|
||||
Id string `json:"id"`
|
||||
RemoteIP string `json:"remoteip"`
|
||||
Hostname string `json:"hostname"`
|
||||
CmdLine string `json:"cmdline"`
|
||||
Pid string `json:"pid"`
|
||||
}
|
||||
|
||||
type gocProfile struct {
|
||||
Profile string `json:"profile"`
|
||||
Profile string `json:"profile"`
|
||||
}
|
||||
|
||||
// NewWorker creates a worker to contact with host
|
||||
func NewWorker(host string) Action {
|
||||
_, err := url.ParseRequestURI(host)
|
||||
if err != nil {
|
||||
log.Fatalf("parse url %s failed, err: %v", host, err)
|
||||
}
|
||||
return &client{
|
||||
Host: host,
|
||||
client: http.DefaultClient,
|
||||
}
|
||||
_, err := url.ParseRequestURI(host)
|
||||
if err != nil {
|
||||
log.Fatalf("parse url %s failed, err: %v", host, err)
|
||||
}
|
||||
return &client{
|
||||
Host: host,
|
||||
client: http.DefaultClient,
|
||||
}
|
||||
}
|
||||
|
||||
// ListAgents Deprecated
|
||||
func (c *client) ListAgents(wide bool) {
|
||||
u := fmt.Sprintf("%s%s", c.Host, CoverAgentsListAPI)
|
||||
_, body, err := c.do("GET", u, "", nil)
|
||||
if err != nil && isNetworkError(err) {
|
||||
_, body, err = c.do("GET", u, "", nil)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("goc list failed: %v", err)
|
||||
}
|
||||
agents := gocListAgents{}
|
||||
err = json.Unmarshal(body, &agents)
|
||||
if err != nil {
|
||||
log.Fatalf("goc list failed: json unmarshal failed: %v", err)
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding(" ") // pad with 3 blank spaces
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetReflowDuringAutoWrap(false)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAutoWrapText(false)
|
||||
if wide {
|
||||
table.SetHeader([]string{"ID", "REMOTEIP", "HOSTNAME", "PID", "CMD"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
} else {
|
||||
table.SetHeader([]string{"ID", "REMOTEIP", "CMD"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
}
|
||||
for _, agent := range agents.Items {
|
||||
if wide {
|
||||
table.Append([]string{agent.Id, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine})
|
||||
} else {
|
||||
preLen := len(agent.Id) + len(agent.RemoteIP) + 9
|
||||
table.Append([]string{agent.Id, agent.RemoteIP, getSimpleCmdLine(preLen, agent.CmdLine)})
|
||||
}
|
||||
}
|
||||
table.Render()
|
||||
return
|
||||
u := fmt.Sprintf("%s%s", c.Host, CoverAgentsListAPI)
|
||||
_, body, err := c.do("GET", u, "", nil)
|
||||
if err != nil && isNetworkError(err) {
|
||||
_, body, err = c.do("GET", u, "", nil)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("goc list failed: %v", err)
|
||||
}
|
||||
agents := gocListAgents{}
|
||||
err = json.Unmarshal(body, &agents)
|
||||
if err != nil {
|
||||
log.Fatalf("goc list failed: json unmarshal failed: %v", err)
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding(" ") // pad with 3 blank spaces
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetReflowDuringAutoWrap(false)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAutoWrapText(false)
|
||||
if wide {
|
||||
table.SetHeader([]string{"ID", "REMOTEIP", "HOSTNAME", "PID", "CMD"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
} else {
|
||||
table.SetHeader([]string{"ID", "REMOTEIP", "CMD"})
|
||||
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
|
||||
}
|
||||
for _, agent := range agents.Items {
|
||||
if wide {
|
||||
table.Append([]string{agent.Id, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine})
|
||||
} else {
|
||||
preLen := len(agent.Id) + len(agent.RemoteIP) + 9
|
||||
table.Append([]string{agent.Id, agent.RemoteIP, getSimpleCmdLine(preLen, agent.CmdLine)})
|
||||
}
|
||||
}
|
||||
table.Render()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *client) Profile(output string) {
|
||||
u := fmt.Sprintf("%s%s", c.Host, CoverProfileAPI)
|
||||
u := fmt.Sprintf("%s%s", c.Host, CoverProfileAPI)
|
||||
|
||||
res, profile, err := c.do("GET", u, "application/json", nil)
|
||||
if err != nil && isNetworkError(err) {
|
||||
res, profile, err = c.do("GET", u, "application/json", nil)
|
||||
}
|
||||
res, profile, err := c.do("GET", u, "application/json", nil)
|
||||
if err != nil && isNetworkError(err) {
|
||||
res, profile, err = c.do("GET", u, "application/json", nil)
|
||||
}
|
||||
|
||||
if err == nil && res.StatusCode != 200 {
|
||||
log.Fatalf(string(profile))
|
||||
}
|
||||
var profileText gocProfile
|
||||
err = json.Unmarshal(profile, &profileText)
|
||||
if err != nil {
|
||||
log.Fatalf("profile unmarshal failed: %v", err)
|
||||
}
|
||||
if output == "" {
|
||||
fmt.Fprint(os.Stdout, profileText.Profile)
|
||||
} else {
|
||||
var dir, filename string = filepath.Split(output)
|
||||
if dir != "" {
|
||||
err = os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create directory %s, err:%v", dir, err)
|
||||
}
|
||||
}
|
||||
if filename == "" {
|
||||
output += "coverage.cov"
|
||||
}
|
||||
if err == nil && res.StatusCode != 200 {
|
||||
log.Fatalf(string(profile))
|
||||
}
|
||||
var profileText gocProfile
|
||||
err = json.Unmarshal(profile, &profileText)
|
||||
if err != nil {
|
||||
log.Fatalf("profile unmarshal failed: %v", err)
|
||||
}
|
||||
if output == "" {
|
||||
fmt.Fprint(os.Stdout, profileText.Profile)
|
||||
} else {
|
||||
var dir, filename string = filepath.Split(output)
|
||||
if dir != "" {
|
||||
err = os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create directory %s, err:%v", dir, err)
|
||||
}
|
||||
}
|
||||
if filename == "" {
|
||||
output += "coverage.cov"
|
||||
}
|
||||
|
||||
f, err := os.Create(output)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create file %s, err:%v", output, err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, bytes.NewReader([]byte(profileText.Profile)))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to write file: %v, err: %v", output, err)
|
||||
}
|
||||
}
|
||||
f, err := os.Create(output)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create file %s, err:%v", output, err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, bytes.NewReader([]byte(profileText.Profile)))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to write file: %v, err: %v", output, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getSimpleCmdLine
|
||||
func getSimpleCmdLine(preLen int, cmdLine string) string {
|
||||
pathLen := len(cmdLine)
|
||||
width, _, err := term.GetSize(int(os.Stdin.Fd()))
|
||||
if err != nil || width <= preLen+16 {
|
||||
width = 16 + preLen // show at least 16 words of the command
|
||||
}
|
||||
if pathLen > width-preLen {
|
||||
return cmdLine[:width-preLen]
|
||||
}
|
||||
return cmdLine
|
||||
pathLen := len(cmdLine)
|
||||
width, _, err := term.GetSize(int(os.Stdin.Fd()))
|
||||
if err != nil || width <= preLen+16 {
|
||||
width = 16 + preLen // show at least 16 words of the command
|
||||
}
|
||||
if pathLen > width-preLen {
|
||||
return cmdLine[:width-preLen]
|
||||
}
|
||||
return cmdLine
|
||||
}
|
||||
|
||||
func (c *client) do(method, url, contentType string, body io.Reader) (*http.Response, []byte, error) {
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if contentType != "" {
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
}
|
||||
if contentType != "" {
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
}
|
||||
|
||||
res, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
res, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
responseBody, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return res, nil, err
|
||||
}
|
||||
return res, responseBody, nil
|
||||
responseBody, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return res, nil, err
|
||||
}
|
||||
return res, responseBody, nil
|
||||
}
|
||||
|
||||
func isNetworkError(err error) bool {
|
||||
if err == io.EOF {
|
||||
return true
|
||||
}
|
||||
_, ok := err.(net.Error)
|
||||
return ok
|
||||
if err == io.EOF {
|
||||
return true
|
||||
}
|
||||
_, ok := err.(net.Error)
|
||||
return ok
|
||||
}
|
||||
|
@ -14,61 +14,61 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/client/rest"
|
||||
"github.com/RickLeee/goc/v2/pkg/client/rest/profile"
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/client/rest"
|
||||
"github.com/ar0c/goc/v2/pkg/client/rest/profile"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
)
|
||||
|
||||
func GetProfile(host string, ids []string, skips []string, extra string, output string, need []string) {
|
||||
gocClient := rest.NewV2Client(host)
|
||||
gocClient := rest.NewV2Client(host)
|
||||
|
||||
profiles, err := gocClient.Profile().Get(ids,
|
||||
profile.WithPackagePattern(skips),
|
||||
profile.WithExtraPattern(extra),
|
||||
profile.WithNeed(need))
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get profile from the goc server: %v, response: %v", err, profiles)
|
||||
}
|
||||
profiles, err := gocClient.Profile().Get(ids,
|
||||
profile.WithPackagePattern(skips),
|
||||
profile.WithExtraPattern(extra),
|
||||
profile.WithNeed(need))
|
||||
if err != nil {
|
||||
log.Fatalf("fail to get profile from the goc server: %v, response: %v", err, profiles)
|
||||
}
|
||||
|
||||
if output == "" {
|
||||
fmt.Fprint(os.Stdout, profiles)
|
||||
} else {
|
||||
var dir, filename string = filepath.Split(output)
|
||||
if dir != "" {
|
||||
err = os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create directory %s, err:%v", dir, err)
|
||||
}
|
||||
}
|
||||
if filename == "" {
|
||||
output += "coverage.cov"
|
||||
}
|
||||
if output == "" {
|
||||
fmt.Fprint(os.Stdout, profiles)
|
||||
} else {
|
||||
var dir, filename string = filepath.Split(output)
|
||||
if dir != "" {
|
||||
err = os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create directory %s, err:%v", dir, err)
|
||||
}
|
||||
}
|
||||
if filename == "" {
|
||||
output += "coverage.cov"
|
||||
}
|
||||
|
||||
f, err := os.Create(output)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create file %s, err:%v", output, err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, bytes.NewReader([]byte(profiles)))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to write file: %v, err: %v", output, err)
|
||||
}
|
||||
}
|
||||
f, err := os.Create(output)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create file %s, err:%v", output, err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, bytes.NewReader([]byte(profiles)))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to write file: %v, err: %v", output, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ClearProfile(host string, ids []string, extra string) {
|
||||
gocClient := rest.NewV2Client(host)
|
||||
gocClient := rest.NewV2Client(host)
|
||||
|
||||
err := gocClient.Profile().Delete(ids,
|
||||
profile.WithExtraPattern(extra))
|
||||
err := gocClient.Profile().Delete(ids,
|
||||
profile.WithExtraPattern(extra))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("fail to clear the profile: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("fail to clear the profile: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -14,26 +14,26 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/RickLeee/goc/v2/pkg/client/rest/agent"
|
||||
"github.com/RickLeee/goc/v2/pkg/client/rest/profile"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/ar0c/goc/v2/pkg/client/rest/agent"
|
||||
"github.com/ar0c/goc/v2/pkg/client/rest/profile"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
// V2Client provides methods contact with the covered agent under test
|
||||
type V2Client struct {
|
||||
rest *resty.Client
|
||||
rest *resty.Client
|
||||
}
|
||||
|
||||
func NewV2Client(host string) *V2Client {
|
||||
return &V2Client{
|
||||
rest: resty.New().SetHostURL("http://" + host),
|
||||
}
|
||||
return &V2Client{
|
||||
rest: resty.New().SetHostURL("http://" + host),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *V2Client) Agent() agent.AgentInterface {
|
||||
return agent.NewAgentsClient(c.rest)
|
||||
return agent.NewAgentsClient(c.rest)
|
||||
}
|
||||
|
||||
func (c *V2Client) Profile() profile.ProfileInterface {
|
||||
return profile.NewProfileClient(c.rest)
|
||||
return profile.NewProfileClient(c.rest)
|
||||
}
|
||||
|
@ -14,390 +14,390 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/tools/cover"
|
||||
"k8s.io/test-infra/gopherage/pkg/cov"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/tools/cover"
|
||||
"k8s.io/test-infra/gopherage/pkg/cov"
|
||||
)
|
||||
|
||||
// listAgents return all service informations
|
||||
func (gs *gocServer) listAgents(c *gin.Context) {
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
|
||||
agents := make([]*gocCoveredAgent, 0)
|
||||
agents := make([]*gocCoveredAgent, 0)
|
||||
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
// check if id is in the query ids
|
||||
if !ifInIdMap(key.(string)) {
|
||||
return true
|
||||
}
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
// check if id is in the query ids
|
||||
if !ifInIdMap(key.(string)) {
|
||||
return true
|
||||
}
|
||||
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
agents = append(agents, agent)
|
||||
return true
|
||||
})
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
agents = append(agents, agent)
|
||||
return true
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"items": agents,
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"items": agents,
|
||||
})
|
||||
}
|
||||
|
||||
func (gs *gocServer) removeAgents(c *gin.Context) {
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
|
||||
errs := ""
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
errs := ""
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
|
||||
// check if id is in the query ids
|
||||
id := key.(string)
|
||||
if !ifInIdMap(id) {
|
||||
return true
|
||||
}
|
||||
// check if id is in the query ids
|
||||
id := key.(string)
|
||||
if !ifInIdMap(id) {
|
||||
return true
|
||||
}
|
||||
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
err := gs.removeAgentFromStore(id)
|
||||
if err != nil {
|
||||
log.Errorf("fail to remove agent: %v", id)
|
||||
err := fmt.Errorf("fail to remove agent: %v, err: %v", id, err)
|
||||
errs = errs + err.Error()
|
||||
return true
|
||||
}
|
||||
agent.closeConnection()
|
||||
gs.agents.Delete(key)
|
||||
err := gs.removeAgentFromStore(id)
|
||||
if err != nil {
|
||||
log.Errorf("fail to remove agent: %v", id)
|
||||
err := fmt.Errorf("fail to remove agent: %v, err: %v", id, err)
|
||||
errs = errs + err.Error()
|
||||
return true
|
||||
}
|
||||
agent.closeConnection()
|
||||
gs.agents.Delete(key)
|
||||
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
if errs != "" {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": errs,
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
if errs != "" {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": errs,
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// getProfiles get and merge all agents' informations
|
||||
//
|
||||
// it is synchronous
|
||||
func (gs *gocServer) getProfiles(c *gin.Context) {
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
|
||||
skippatternRaw := c.Query("skippattern")
|
||||
var skippattern []string
|
||||
if skippatternRaw != "" {
|
||||
skippattern = strings.Split(skippatternRaw, ",")
|
||||
}
|
||||
neerpatternRaw := c.Query("needpattern")
|
||||
var neerpattern []string
|
||||
if neerpatternRaw != "" {
|
||||
neerpattern = strings.Split(neerpatternRaw, ",")
|
||||
}
|
||||
skippatternRaw := c.Query("skippattern")
|
||||
var skippattern []string
|
||||
if skippatternRaw != "" {
|
||||
skippattern = strings.Split(skippatternRaw, ",")
|
||||
}
|
||||
neerpatternRaw := c.Query("needpattern")
|
||||
var neerpattern []string
|
||||
if neerpatternRaw != "" {
|
||||
neerpattern = strings.Split(neerpatternRaw, ",")
|
||||
}
|
||||
|
||||
extra := c.Query("extra")
|
||||
isExtra := filterExtra(extra)
|
||||
extra := c.Query("extra")
|
||||
isExtra := filterExtra(extra)
|
||||
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
|
||||
mergedProfiles := make([][]*cover.Profile, 0)
|
||||
mergedProfiles := make([][]*cover.Profile, 0)
|
||||
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
// check if id is in the query ids
|
||||
if !ifInIdMap(key.(string)) {
|
||||
// not in
|
||||
return true
|
||||
}
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
// check if id is in the query ids
|
||||
if !ifInIdMap(key.(string)) {
|
||||
// not in
|
||||
return true
|
||||
}
|
||||
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if extra matches
|
||||
if !isExtra(agent.Extra) {
|
||||
// not match
|
||||
return true
|
||||
}
|
||||
// check if extra matches
|
||||
if !isExtra(agent.Extra) {
|
||||
// not match
|
||||
return true
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
// 并发 rpc,且每个 rpc 设超时时间 10 second
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wg.Add(1)
|
||||
// 并发 rpc,且每个 rpc 设超时时间 10 second
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
timeout := time.Duration(10 * time.Second)
|
||||
done := make(chan error, 1)
|
||||
timeout := time.Duration(10 * time.Second)
|
||||
done := make(chan error, 1)
|
||||
|
||||
var req ProfileReq = "getprofile"
|
||||
var res ProfileRes
|
||||
go func() {
|
||||
// lock-free
|
||||
rpc := agent.rpc
|
||||
if rpc == nil || agent.Status == DISCONNECT {
|
||||
done <- nil
|
||||
return
|
||||
}
|
||||
err := agent.rpc.Call("GocAgent.GetProfile", req, &res)
|
||||
if err != nil {
|
||||
log.Errorf("fail to get profile from: %v, reasson: %v. let's close the connection", agent.Id, err)
|
||||
}
|
||||
done <- err
|
||||
}()
|
||||
var req ProfileReq = "getprofile"
|
||||
var res ProfileRes
|
||||
go func() {
|
||||
// lock-free
|
||||
rpc := agent.rpc
|
||||
if rpc == nil || agent.Status == DISCONNECT {
|
||||
done <- nil
|
||||
return
|
||||
}
|
||||
err := agent.rpc.Call("GocAgent.GetProfile", req, &res)
|
||||
if err != nil {
|
||||
log.Errorf("fail to get profile from: %v, reasson: %v. let's close the connection", agent.Id, err)
|
||||
}
|
||||
done <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
// rpc 超时
|
||||
case <-time.After(timeout):
|
||||
log.Warnf("rpc call timeout: %v", agent.Hostname)
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
case err := <-done:
|
||||
// 调用 rpc 发生错误
|
||||
if err != nil {
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
}
|
||||
}
|
||||
// append profile
|
||||
profile, err := convertProfile([]byte(res))
|
||||
if err != nil {
|
||||
log.Errorf("fail to convert the received profile from: %v, reasson: %v. let's close the connection", agent.Id, err)
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
return
|
||||
}
|
||||
select {
|
||||
// rpc 超时
|
||||
case <-time.After(timeout):
|
||||
log.Warnf("rpc call timeout: %v", agent.Hostname)
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
case err := <-done:
|
||||
// 调用 rpc 发生错误
|
||||
if err != nil {
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
}
|
||||
}
|
||||
// append profile
|
||||
profile, err := convertProfile([]byte(res))
|
||||
if err != nil {
|
||||
log.Errorf("fail to convert the received profile from: %v, reasson: %v. let's close the connection", agent.Id, err)
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
return
|
||||
}
|
||||
|
||||
// check if skippattern matches
|
||||
newProfile := filterProfileByPattern(skippattern, neerpattern, profile)
|
||||
// check if skippattern matches
|
||||
newProfile := filterProfileByPattern(skippattern, neerpattern, profile)
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
mergedProfiles = append(mergedProfiles, newProfile)
|
||||
}()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
mergedProfiles = append(mergedProfiles, newProfile)
|
||||
}()
|
||||
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
// 一直等待并发的 rpc 都回应
|
||||
wg.Wait()
|
||||
// 一直等待并发的 rpc 都回应
|
||||
wg.Wait()
|
||||
|
||||
merged, err := cov.MergeMultipleProfiles(mergedProfiles)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
merged, err := cov.MergeMultipleProfiles(mergedProfiles)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var buff bytes.Buffer
|
||||
err = cov.DumpProfile(merged, &buff)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
var buff bytes.Buffer
|
||||
err = cov.DumpProfile(merged, &buff)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"profile": buff.String(),
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"profile": buff.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// resetProfiles reset all profiles in agent
|
||||
//
|
||||
// it is async, the function will return immediately
|
||||
func (gs *gocServer) resetProfiles(c *gin.Context) {
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
idQuery := c.Query("id")
|
||||
ifInIdMap := idMaps(idQuery)
|
||||
|
||||
extra := c.Query("extra")
|
||||
isExtra := filterExtra(extra)
|
||||
extra := c.Query("extra")
|
||||
isExtra := filterExtra(extra)
|
||||
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
gs.agents.Range(func(key, value interface{}) bool {
|
||||
|
||||
// check if id is in the query ids
|
||||
if !ifInIdMap(key.(string)) {
|
||||
// not in
|
||||
return true
|
||||
}
|
||||
// check if id is in the query ids
|
||||
if !ifInIdMap(key.(string)) {
|
||||
// not in
|
||||
return true
|
||||
}
|
||||
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
agent, ok := value.(*gocCoveredAgent)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if extra matches
|
||||
if !isExtra(agent.Extra) {
|
||||
// not match
|
||||
return true
|
||||
}
|
||||
// check if extra matches
|
||||
if !isExtra(agent.Extra) {
|
||||
// not match
|
||||
return true
|
||||
}
|
||||
|
||||
var req ProfileReq = "resetprofile"
|
||||
var res ProfileRes
|
||||
go func() {
|
||||
// lock-free
|
||||
rpc := agent.rpc
|
||||
if rpc == nil || agent.Status == DISCONNECT {
|
||||
return
|
||||
}
|
||||
err := rpc.Call("GocAgent.ResetProfile", req, &res)
|
||||
if err != nil {
|
||||
log.Errorf("fail to reset profile from: %v, reasson: %v. let's close the connection", agent.Id, err)
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
}
|
||||
}()
|
||||
var req ProfileReq = "resetprofile"
|
||||
var res ProfileRes
|
||||
go func() {
|
||||
// lock-free
|
||||
rpc := agent.rpc
|
||||
if rpc == nil || agent.Status == DISCONNECT {
|
||||
return
|
||||
}
|
||||
err := rpc.Call("GocAgent.ResetProfile", req, &res)
|
||||
if err != nil {
|
||||
log.Errorf("fail to reset profile from: %v, reasson: %v. let's close the connection", agent.Id, err)
|
||||
// 关闭链接
|
||||
agent.closeRpcConnOnce()
|
||||
}
|
||||
}()
|
||||
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// watchProfileUpdate watch the profile change
|
||||
//
|
||||
// any profile change will be updated on this websocket connection.
|
||||
func (gs *gocServer) watchProfileUpdate(c *gin.Context) {
|
||||
// upgrade to websocket
|
||||
ws, err := gs.upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
log.Errorf("fail to establish websocket connection with watch client: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
}
|
||||
// upgrade to websocket
|
||||
ws, err := gs.upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
log.Errorf("fail to establish websocket connection with watch client: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
}
|
||||
|
||||
log.Infof("watch client connected")
|
||||
log.Infof("watch client connected")
|
||||
|
||||
id := time.Now().String()
|
||||
gwc := &gocWatchClient{
|
||||
ws: ws,
|
||||
exitCh: make(chan int),
|
||||
}
|
||||
gs.watchClients.Store(id, gwc)
|
||||
// send close msg and close ws connection
|
||||
defer func() {
|
||||
gs.watchClients.Delete(id)
|
||||
ws.Close()
|
||||
gwc.once.Do(func() { close(gwc.exitCh) })
|
||||
log.Infof("watch client disconnected")
|
||||
}()
|
||||
id := time.Now().String()
|
||||
gwc := &gocWatchClient{
|
||||
ws: ws,
|
||||
exitCh: make(chan int),
|
||||
}
|
||||
gs.watchClients.Store(id, gwc)
|
||||
// send close msg and close ws connection
|
||||
defer func() {
|
||||
gs.watchClients.Delete(id)
|
||||
ws.Close()
|
||||
gwc.once.Do(func() { close(gwc.exitCh) })
|
||||
log.Infof("watch client disconnected")
|
||||
}()
|
||||
|
||||
// set pong handler
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
ws.SetPongHandler(func(string) error {
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
return nil
|
||||
})
|
||||
// set pong handler
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
ws.SetPongHandler(func(string) error {
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
return nil
|
||||
})
|
||||
|
||||
// set ping goroutine to ping every PingWait time
|
||||
go func() {
|
||||
ticker := time.NewTicker(PingWait)
|
||||
defer ticker.Stop()
|
||||
// set ping goroutine to ping every PingWait time
|
||||
go func() {
|
||||
ticker := time.NewTicker(PingWait)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if err := gs.wsping(ws, PongWait); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
for range ticker.C {
|
||||
if err := gs.wsping(ws, PongWait); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
gwc.once.Do(func() { close(gwc.exitCh) })
|
||||
}()
|
||||
gwc.once.Do(func() { close(gwc.exitCh) })
|
||||
}()
|
||||
|
||||
<-gwc.exitCh
|
||||
<-gwc.exitCh
|
||||
}
|
||||
|
||||
func filterProfileByPattern(skippattern []string, needpattern []string, profiles []*cover.Profile) []*cover.Profile {
|
||||
|
||||
var out = make([]*cover.Profile, 0)
|
||||
var skipOut = make([]*cover.Profile, 0)
|
||||
if len(skippattern) == 0 && len(needpattern) == 0 {
|
||||
return profiles
|
||||
}
|
||||
if len(skippattern) != 0 {
|
||||
for _, profile := range profiles {
|
||||
skip := false
|
||||
for _, pattern := range skippattern {
|
||||
if strings.Contains(profile.FileName, pattern) {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
var out = make([]*cover.Profile, 0)
|
||||
var skipOut = make([]*cover.Profile, 0)
|
||||
if len(skippattern) == 0 && len(needpattern) == 0 {
|
||||
return profiles
|
||||
}
|
||||
if len(skippattern) != 0 {
|
||||
for _, profile := range profiles {
|
||||
skip := false
|
||||
for _, pattern := range skippattern {
|
||||
if strings.Contains(profile.FileName, pattern) {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !skip {
|
||||
skipOut = append(skipOut, profile)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skipOut = profiles
|
||||
}
|
||||
log.Infof("skipOut len: %v", len(skipOut))
|
||||
if len(needpattern) == 0 {
|
||||
return skipOut
|
||||
}
|
||||
if !skip {
|
||||
skipOut = append(skipOut, profile)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skipOut = profiles
|
||||
}
|
||||
log.Infof("skipOut len: %v", len(skipOut))
|
||||
if len(needpattern) == 0 {
|
||||
return skipOut
|
||||
}
|
||||
|
||||
for _, profile := range skipOut {
|
||||
need := false
|
||||
for _, pattern := range needpattern {
|
||||
if strings.Contains(profile.FileName, pattern) {
|
||||
need = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if need {
|
||||
out = append(out, profile)
|
||||
}
|
||||
}
|
||||
log.Infof("need out len: %v", len(out))
|
||||
for _, profile := range skipOut {
|
||||
need := false
|
||||
for _, pattern := range needpattern {
|
||||
if strings.Contains(profile.FileName, pattern) {
|
||||
need = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if need {
|
||||
out = append(out, profile)
|
||||
}
|
||||
}
|
||||
log.Infof("need out len: %v", len(out))
|
||||
|
||||
return out
|
||||
return out
|
||||
}
|
||||
|
||||
func idMaps(idQuery string) func(key string) bool {
|
||||
idMap := make(map[string]bool)
|
||||
if len(strings.TrimSpace(idQuery)) == 0 {
|
||||
} else {
|
||||
ids := strings.Split(idQuery, ",")
|
||||
for _, id := range ids {
|
||||
idMap[id] = true
|
||||
}
|
||||
}
|
||||
idMap := make(map[string]bool)
|
||||
if len(strings.TrimSpace(idQuery)) == 0 {
|
||||
} else {
|
||||
ids := strings.Split(idQuery, ",")
|
||||
for _, id := range ids {
|
||||
idMap[id] = true
|
||||
}
|
||||
}
|
||||
|
||||
inIdMaps := func(key string) bool {
|
||||
// if no id in query, then all id agent will be return
|
||||
if len(idMap) == 0 {
|
||||
return true
|
||||
}
|
||||
// other
|
||||
_, ok := idMap[key]
|
||||
if !ok {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
inIdMaps := func(key string) bool {
|
||||
// if no id in query, then all id agent will be return
|
||||
if len(idMap) == 0 {
|
||||
return true
|
||||
}
|
||||
// other
|
||||
_, ok := idMap[key]
|
||||
if !ok {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return inIdMaps
|
||||
return inIdMaps
|
||||
}
|
||||
|
||||
func filterExtra(extraPattern string) func(string) bool {
|
||||
|
||||
re := regexp.MustCompile(extraPattern)
|
||||
re := regexp.MustCompile(extraPattern)
|
||||
|
||||
return func(extra string) bool {
|
||||
return re.Match([]byte(extra))
|
||||
}
|
||||
return func(extra string) bool {
|
||||
return re.Match([]byte(extra))
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,16 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"sync"
|
||||
"time"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// serveRpcStream holds connection between goc server and agent.
|
||||
@ -32,114 +32,114 @@ import (
|
||||
//
|
||||
// 2. 每个链接的 goc agent 作为 rpc 服务端
|
||||
func (gs *gocServer) serveRpcStream(c *gin.Context) {
|
||||
// 检查插桩服务上报的信息
|
||||
rpcRemoteIP, _ := c.RemoteIP()
|
||||
id := c.Query("id")
|
||||
token := c.Query("token")
|
||||
// 检查插桩服务上报的信息
|
||||
rpcRemoteIP, _ := c.RemoteIP()
|
||||
id := c.Query("id")
|
||||
token := c.Query("token")
|
||||
|
||||
rawagent, ok := gs.agents.Load(id)
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "agent not registered",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
rawagent, ok := gs.agents.Load(id)
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "agent not registered",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
agent := rawagent.(*gocCoveredAgent)
|
||||
if agent.Token != token {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "register token not match",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
agent := rawagent.(*gocCoveredAgent)
|
||||
if agent.Token != token {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "register token not match",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新 agent 信息
|
||||
agent.RpcRemoteIP = rpcRemoteIP.String()
|
||||
agent.exitCh = make(chan int)
|
||||
agent.Status &= ^DISCONNECT // 取消 DISCONNECT 的状态
|
||||
agent.Status |= RPCCONNECT // 设置为 RPC CONNECT 状态
|
||||
// 注册销毁函数
|
||||
var once sync.Once
|
||||
agent.closeRpcConnOnce = func() {
|
||||
once.Do(func() {
|
||||
// 为什么只是关闭 channel?其它资源如何释放?
|
||||
// close channel 后,本 goroutine 会进入到 defer
|
||||
close(agent.exitCh)
|
||||
})
|
||||
}
|
||||
// 更新 agent 信息
|
||||
agent.RpcRemoteIP = rpcRemoteIP.String()
|
||||
agent.exitCh = make(chan int)
|
||||
agent.Status &= ^DISCONNECT // 取消 DISCONNECT 的状态
|
||||
agent.Status |= RPCCONNECT // 设置为 RPC CONNECT 状态
|
||||
// 注册销毁函数
|
||||
var once sync.Once
|
||||
agent.closeRpcConnOnce = func() {
|
||||
once.Do(func() {
|
||||
// 为什么只是关闭 channel?其它资源如何释放?
|
||||
// close channel 后,本 goroutine 会进入到 defer
|
||||
close(agent.exitCh)
|
||||
})
|
||||
}
|
||||
|
||||
// upgrade to websocket
|
||||
ws, err := gs.upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
log.Errorf("fail to establish websocket connection with rpc agent: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
}
|
||||
// upgrade to websocket
|
||||
ws, err := gs.upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
log.Errorf("fail to establish websocket connection with rpc agent: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
}
|
||||
|
||||
// send close msg and close ws connection
|
||||
defer func() {
|
||||
deadline := 1 * time.Second
|
||||
// 发送 close msg
|
||||
gs.wsclose(ws, deadline)
|
||||
time.Sleep(deadline)
|
||||
// send close msg and close ws connection
|
||||
defer func() {
|
||||
deadline := 1 * time.Second
|
||||
// 发送 close msg
|
||||
gs.wsclose(ws, deadline)
|
||||
time.Sleep(deadline)
|
||||
|
||||
// 取消 RPC CONNECT 状态
|
||||
agent.Status &= ^RPCCONNECT
|
||||
if agent.Status == 0 {
|
||||
agent.Status = DISCONNECT
|
||||
}
|
||||
// 取消 RPC CONNECT 状态
|
||||
agent.Status &= ^RPCCONNECT
|
||||
if agent.Status == 0 {
|
||||
agent.Status = DISCONNECT
|
||||
}
|
||||
|
||||
ws.Close()
|
||||
log.Infof("close rpc connection, %v", agent.Hostname)
|
||||
// reset rpc client
|
||||
agent.rpc = nil
|
||||
}()
|
||||
ws.Close()
|
||||
log.Infof("close rpc connection, %v", agent.Hostname)
|
||||
// reset rpc client
|
||||
agent.rpc = nil
|
||||
}()
|
||||
|
||||
// set pong handler
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
ws.SetPongHandler(func(string) error {
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
return nil
|
||||
})
|
||||
// set pong handler
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
ws.SetPongHandler(func(string) error {
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
return nil
|
||||
})
|
||||
|
||||
// set ping goroutine to ping every PingWait time
|
||||
go func() {
|
||||
ticker := time.NewTicker(PingWait)
|
||||
defer ticker.Stop()
|
||||
// set ping goroutine to ping every PingWait time
|
||||
go func() {
|
||||
ticker := time.NewTicker(PingWait)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if err := gs.wsping(ws, PongWait); err != nil {
|
||||
log.Errorf("rpc ping to %v failed: %v", agent.Hostname, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
for range ticker.C {
|
||||
if err := gs.wsping(ws, PongWait); err != nil {
|
||||
log.Errorf("rpc ping to %v failed: %v", agent.Hostname, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
agent.closeRpcConnOnce()
|
||||
}()
|
||||
agent.closeRpcConnOnce()
|
||||
}()
|
||||
|
||||
log.Infof("one rpc agent established, %v, cmdline: %v, pid: %v, hostname: %v", ws.RemoteAddr(), agent.CmdLine, agent.Pid, agent.Hostname)
|
||||
// new rpc agent
|
||||
// 在这里 websocket server 作为 rpc 的客户端,
|
||||
// 发送 rpc 请求,
|
||||
// 由被插桩服务返回 rpc 应答
|
||||
rwc := &ReadWriteCloser{ws: ws}
|
||||
codec := jsonrpc.NewClientCodec(rwc)
|
||||
log.Infof("one rpc agent established, %v, cmdline: %v, pid: %v, hostname: %v", ws.RemoteAddr(), agent.CmdLine, agent.Pid, agent.Hostname)
|
||||
// new rpc agent
|
||||
// 在这里 websocket server 作为 rpc 的客户端,
|
||||
// 发送 rpc 请求,
|
||||
// 由被插桩服务返回 rpc 应答
|
||||
rwc := &ReadWriteCloser{ws: ws}
|
||||
codec := jsonrpc.NewClientCodec(rwc)
|
||||
|
||||
agent.rpc = rpc.NewClientWithCodec(codec)
|
||||
agent.rpc = rpc.NewClientWithCodec(codec)
|
||||
|
||||
// wait for exit
|
||||
<-agent.exitCh
|
||||
// wait for exit
|
||||
<-agent.exitCh
|
||||
}
|
||||
|
||||
// generateAgentId generate id based on agent's meta infomation
|
||||
func (gs *gocServer) generateAgentId(args ...string) gocCliendId {
|
||||
var path string
|
||||
for _, arg := range args {
|
||||
path += arg
|
||||
}
|
||||
sum := sha256.Sum256([]byte(path))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
var path string
|
||||
for _, arg := range args {
|
||||
path += arg
|
||||
}
|
||||
sum := sha256.Sum256([]byte(path))
|
||||
h := fmt.Sprintf("%x", sum[:6])
|
||||
|
||||
return gocCliendId(h)
|
||||
return gocCliendId(h)
|
||||
}
|
||||
|
@ -14,236 +14,236 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/RickLeee/goc/v2/pkg/server/store"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/ar0c/goc/v2/pkg/server/store"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// gocServer represents a goc server
|
||||
type gocServer struct {
|
||||
port int
|
||||
store store.Store
|
||||
port int
|
||||
store store.Store
|
||||
|
||||
upgrader websocket.Upgrader
|
||||
upgrader websocket.Upgrader
|
||||
|
||||
agents sync.Map
|
||||
agents sync.Map
|
||||
|
||||
watchCh chan []byte
|
||||
watchClients sync.Map
|
||||
watchCh chan []byte
|
||||
watchClients sync.Map
|
||||
|
||||
idCount int64
|
||||
idL sync.Mutex
|
||||
idCount int64
|
||||
idL sync.Mutex
|
||||
}
|
||||
|
||||
type gocCliendId string
|
||||
|
||||
const (
|
||||
DISCONNECT = 1 << iota
|
||||
RPCCONNECT = 1 << iota
|
||||
WATCHCONNECT = 1 << iota
|
||||
DISCONNECT = 1 << iota
|
||||
RPCCONNECT = 1 << iota
|
||||
WATCHCONNECT = 1 << iota
|
||||
)
|
||||
|
||||
// gocCoveredAgent represents a covered client
|
||||
type gocCoveredAgent struct {
|
||||
Id string `json:"id"`
|
||||
RpcRemoteIP string `json:"rpc_remoteip"`
|
||||
WatchRemoteIP string `json:"watch_remoteip"`
|
||||
Hostname string `json:"hostname"`
|
||||
CmdLine string `json:"cmdline"`
|
||||
Pid string `json:"pid"`
|
||||
Id string `json:"id"`
|
||||
RpcRemoteIP string `json:"rpc_remoteip"`
|
||||
WatchRemoteIP string `json:"watch_remoteip"`
|
||||
Hostname string `json:"hostname"`
|
||||
CmdLine string `json:"cmdline"`
|
||||
Pid string `json:"pid"`
|
||||
|
||||
// 用户可以选择上报一些定制信息
|
||||
// 比如不同 namespace 的 statefulset POD,它们的 hostname/cmdline/pid 都是一样的,
|
||||
// 这时候将 extra 设置为 namespace 并上报,这个额外的信息在展示时将更友好
|
||||
Extra string `json:"extra"`
|
||||
// 用户可以选择上报一些定制信息
|
||||
// 比如不同 namespace 的 statefulset POD,它们的 hostname/cmdline/pid 都是一样的,
|
||||
// 这时候将 extra 设置为 namespace 并上报,这个额外的信息在展示时将更友好
|
||||
Extra string `json:"extra"`
|
||||
|
||||
Token string `json:"token"`
|
||||
Status int `json:"status"` // 表示该 agent 是否处于 connected 状态
|
||||
Token string `json:"token"`
|
||||
Status int `json:"status"` // 表示该 agent 是否处于 connected 状态
|
||||
|
||||
rpc *rpc.Client `json:"-"`
|
||||
rpc *rpc.Client `json:"-"`
|
||||
|
||||
exitCh chan int `json:"-"`
|
||||
closeRpcConnOnce func() `json:"-"` // close rpc conn 只执行一次
|
||||
closeWatchConnOnce func() `json:"-"` // close watch conn 只执行一次
|
||||
exitCh chan int `json:"-"`
|
||||
closeRpcConnOnce func() `json:"-"` // close rpc conn 只执行一次
|
||||
closeWatchConnOnce func() `json:"-"` // close watch conn 只执行一次
|
||||
}
|
||||
|
||||
func (agent *gocCoveredAgent) closeConnection() {
|
||||
if agent.closeRpcConnOnce != nil {
|
||||
agent.closeRpcConnOnce()
|
||||
}
|
||||
if agent.closeRpcConnOnce != nil {
|
||||
agent.closeRpcConnOnce()
|
||||
}
|
||||
|
||||
if agent.closeWatchConnOnce != nil {
|
||||
agent.closeWatchConnOnce()
|
||||
}
|
||||
if agent.closeWatchConnOnce != nil {
|
||||
agent.closeWatchConnOnce()
|
||||
}
|
||||
}
|
||||
|
||||
// api 客户端,不是 agent
|
||||
type gocWatchClient struct {
|
||||
ws *websocket.Conn
|
||||
exitCh chan int
|
||||
once sync.Once
|
||||
ws *websocket.Conn
|
||||
exitCh chan int
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func RunGocServerUntilExit(host string, s store.Store) error {
|
||||
gs := gocServer{
|
||||
store: s,
|
||||
upgrader: websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
},
|
||||
watchCh: make(chan []byte, 4096),
|
||||
}
|
||||
gs := gocServer{
|
||||
store: s,
|
||||
upgrader: websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
},
|
||||
watchCh: make(chan []byte, 4096),
|
||||
}
|
||||
|
||||
// 从持久化存储上恢复 agent 列表
|
||||
gs.restoreAgents()
|
||||
// 从持久化存储上恢复 agent 列表
|
||||
gs.restoreAgents()
|
||||
|
||||
r := gin.Default()
|
||||
v2 := r.Group("/v2")
|
||||
{
|
||||
v2.GET("/cover/profile", gs.getProfiles)
|
||||
v2.DELETE("/cover/profile", gs.resetProfiles)
|
||||
v2.GET("/agents", gs.listAgents)
|
||||
v2.DELETE("/agents", gs.removeAgents)
|
||||
r := gin.Default()
|
||||
v2 := r.Group("/v2")
|
||||
{
|
||||
v2.GET("/cover/profile", gs.getProfiles)
|
||||
v2.DELETE("/cover/profile", gs.resetProfiles)
|
||||
v2.GET("/agents", gs.listAgents)
|
||||
v2.DELETE("/agents", gs.removeAgents)
|
||||
|
||||
v2.GET("/cover/ws/watch", gs.watchProfileUpdate)
|
||||
v2.GET("/cover/ws/watch", gs.watchProfileUpdate)
|
||||
|
||||
// internal use only
|
||||
v2.GET("/internal/register", gs.register)
|
||||
v2.GET("/internal/ws/rpcstream", gs.serveRpcStream)
|
||||
v2.GET("/internal/ws/watchstream", gs.serveWatchInternalStream)
|
||||
}
|
||||
// internal use only
|
||||
v2.GET("/internal/register", gs.register)
|
||||
v2.GET("/internal/ws/rpcstream", gs.serveRpcStream)
|
||||
v2.GET("/internal/ws/watchstream", gs.serveWatchInternalStream)
|
||||
}
|
||||
|
||||
go gs.watchLoop()
|
||||
return r.Run(host)
|
||||
go gs.watchLoop()
|
||||
return r.Run(host)
|
||||
}
|
||||
|
||||
func (gs *gocServer) register(c *gin.Context) {
|
||||
// 检查插桩服务上报的信息
|
||||
hostname := c.Query("hostname")
|
||||
pid := c.Query("pid")
|
||||
cmdline := c.Query("cmdline")
|
||||
extra := c.Query("extra")
|
||||
// 检查插桩服务上报的信息
|
||||
hostname := c.Query("hostname")
|
||||
pid := c.Query("pid")
|
||||
cmdline := c.Query("cmdline")
|
||||
extra := c.Query("extra")
|
||||
|
||||
if hostname == "" || pid == "" || cmdline == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "missing some params",
|
||||
})
|
||||
return
|
||||
}
|
||||
if hostname == "" || pid == "" || cmdline == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "missing some params",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
gs.idL.Lock()
|
||||
gs.idCount++
|
||||
globalId := gs.idCount
|
||||
gs.idL.Unlock()
|
||||
gs.idL.Lock()
|
||||
gs.idCount++
|
||||
globalId := gs.idCount
|
||||
gs.idL.Unlock()
|
||||
|
||||
genToken := func(i int64) string {
|
||||
now := time.Now().UnixNano()
|
||||
random := rand.Int()
|
||||
genToken := func(i int64) string {
|
||||
now := time.Now().UnixNano()
|
||||
random := rand.Int()
|
||||
|
||||
raw := fmt.Sprintf("%v-%v-%v", i, random, now)
|
||||
sum := sha256.Sum256([]byte(raw))
|
||||
h := fmt.Sprintf("%x", sum[:16])
|
||||
raw := fmt.Sprintf("%v-%v-%v", i, random, now)
|
||||
sum := sha256.Sum256([]byte(raw))
|
||||
h := fmt.Sprintf("%x", sum[:16])
|
||||
|
||||
return h
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
token := genToken(globalId)
|
||||
id := strconv.Itoa(int(globalId))
|
||||
token := genToken(globalId)
|
||||
id := strconv.Itoa(int(globalId))
|
||||
|
||||
agent := &gocCoveredAgent{
|
||||
Id: id,
|
||||
Hostname: hostname,
|
||||
Pid: pid,
|
||||
CmdLine: cmdline,
|
||||
Token: token,
|
||||
Status: DISCONNECT,
|
||||
Extra: extra,
|
||||
}
|
||||
agent := &gocCoveredAgent{
|
||||
Id: id,
|
||||
Hostname: hostname,
|
||||
Pid: pid,
|
||||
CmdLine: cmdline,
|
||||
Token: token,
|
||||
Status: DISCONNECT,
|
||||
Extra: extra,
|
||||
}
|
||||
|
||||
// 持久化
|
||||
err := gs.saveAgentToStore(agent)
|
||||
if err != nil {
|
||||
log.Errorf("fail to save to store: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": err.Error(),
|
||||
})
|
||||
}
|
||||
// 维护 agent 连接
|
||||
gs.agents.Store(id, agent)
|
||||
// 持久化
|
||||
err := gs.saveAgentToStore(agent)
|
||||
if err != nil {
|
||||
log.Errorf("fail to save to store: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"msg": err.Error(),
|
||||
})
|
||||
}
|
||||
// 维护 agent 连接
|
||||
gs.agents.Store(id, agent)
|
||||
|
||||
log.Infof("one agent registered, id: %v, cmdline: %v, pid: %v, hostname: %v", id, agent.CmdLine, agent.Pid, agent.Hostname)
|
||||
log.Infof("one agent registered, id: %v, cmdline: %v, pid: %v, hostname: %v", id, agent.CmdLine, agent.Pid, agent.Hostname)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": id,
|
||||
"token": token,
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": id,
|
||||
"token": token,
|
||||
})
|
||||
}
|
||||
|
||||
func (gs *gocServer) saveAgentToStore(agent *gocCoveredAgent) error {
|
||||
|
||||
value, err := json.Marshal(agent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return gs.store.Set("/goc/agents/"+agent.Id, string(value))
|
||||
value, err := json.Marshal(agent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return gs.store.Set("/goc/agents/"+agent.Id, string(value))
|
||||
}
|
||||
|
||||
func (gs *gocServer) removeAgentFromStore(id string) error {
|
||||
|
||||
return gs.store.Remove("/goc/agents/" + id)
|
||||
return gs.store.Remove("/goc/agents/" + id)
|
||||
}
|
||||
|
||||
func (gs *gocServer) removeAllAgentsFromStore() error {
|
||||
|
||||
return gs.store.RangeRemove("/goc/agents/")
|
||||
return gs.store.RangeRemove("/goc/agents/")
|
||||
}
|
||||
|
||||
func (gs *gocServer) restoreAgents() {
|
||||
pattern := "/goc/agents/"
|
||||
pattern := "/goc/agents/"
|
||||
|
||||
// ignore err, 这个 err 不需要处理,直接忽略
|
||||
rawagents, _ := gs.store.Range(pattern)
|
||||
// ignore err, 这个 err 不需要处理,直接忽略
|
||||
rawagents, _ := gs.store.Range(pattern)
|
||||
|
||||
var maxId int
|
||||
for _, rawagent := range rawagents {
|
||||
var agent gocCoveredAgent
|
||||
err := json.Unmarshal([]byte(rawagent), &agent)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to unmarshal restore agents: %v", err)
|
||||
}
|
||||
var maxId int
|
||||
for _, rawagent := range rawagents {
|
||||
var agent gocCoveredAgent
|
||||
err := json.Unmarshal([]byte(rawagent), &agent)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to unmarshal restore agents: %v", err)
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(agent.Id)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to transform id to number: %v", err)
|
||||
}
|
||||
if maxId < id {
|
||||
maxId = id
|
||||
}
|
||||
id, err := strconv.Atoi(agent.Id)
|
||||
if err != nil {
|
||||
log.Fatalf("fail to transform id to number: %v", err)
|
||||
}
|
||||
if maxId < id {
|
||||
maxId = id
|
||||
}
|
||||
|
||||
gs.agents.Store(agent.Id, &agent)
|
||||
log.Infof("restore one agent: %v, %v from store", id, agent.RpcRemoteIP)
|
||||
gs.agents.Store(agent.Id, &agent)
|
||||
log.Infof("restore one agent: %v, %v from store", id, agent.RpcRemoteIP)
|
||||
|
||||
agent.RpcRemoteIP = ""
|
||||
agent.WatchRemoteIP = ""
|
||||
agent.Status = DISCONNECT
|
||||
}
|
||||
agent.RpcRemoteIP = ""
|
||||
agent.WatchRemoteIP = ""
|
||||
agent.Status = DISCONNECT
|
||||
}
|
||||
|
||||
// 更新全局 id
|
||||
atomic.StoreInt64(&gs.idCount, int64(maxId))
|
||||
// 更新全局 id
|
||||
atomic.StoreInt64(&gs.idCount, int64(maxId))
|
||||
}
|
||||
|
@ -14,120 +14,120 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func (gs *gocServer) serveWatchInternalStream(c *gin.Context) {
|
||||
// 检查插桩服务上报的信息
|
||||
watchRemoteIP, _ := c.RemoteIP()
|
||||
id := c.Query("id")
|
||||
token := c.Query("token")
|
||||
// 检查插桩服务上报的信息
|
||||
watchRemoteIP, _ := c.RemoteIP()
|
||||
id := c.Query("id")
|
||||
token := c.Query("token")
|
||||
|
||||
rawagent, ok := gs.agents.Load(id)
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "agent not registered",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
rawagent, ok := gs.agents.Load(id)
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "agent not registered",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
agent := rawagent.(*gocCoveredAgent)
|
||||
if agent.Token != token {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "register token not match",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
agent := rawagent.(*gocCoveredAgent)
|
||||
if agent.Token != token {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"msg": "register token not match",
|
||||
"code": 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新 agent 信息
|
||||
agent.WatchRemoteIP = watchRemoteIP.String()
|
||||
agent.Status &= ^DISCONNECT // 取消 DISCONNECT 的状态
|
||||
agent.Status |= WATCHCONNECT // 设置为 RPC CONNECT 状态
|
||||
var once sync.Once
|
||||
// 更新 agent 信息
|
||||
agent.WatchRemoteIP = watchRemoteIP.String()
|
||||
agent.Status &= ^DISCONNECT // 取消 DISCONNECT 的状态
|
||||
agent.Status |= WATCHCONNECT // 设置为 RPC CONNECT 状态
|
||||
var once sync.Once
|
||||
|
||||
// upgrade to websocket
|
||||
ws, err := gs.upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
log.Errorf("fail to establish websocket connection with watch agent: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
}
|
||||
// upgrade to websocket
|
||||
ws, err := gs.upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
log.Errorf("fail to establish websocket connection with watch agent: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
}
|
||||
|
||||
// 注册销毁函数
|
||||
agent.closeWatchConnOnce = func() {
|
||||
once.Do(func() {
|
||||
// 关闭 ws 连接后,ws.ReadMessage() 会出错退出 goroutine,进入 defer
|
||||
ws.Close()
|
||||
})
|
||||
}
|
||||
// 注册销毁函数
|
||||
agent.closeWatchConnOnce = func() {
|
||||
once.Do(func() {
|
||||
// 关闭 ws 连接后,ws.ReadMessage() 会出错退出 goroutine,进入 defer
|
||||
ws.Close()
|
||||
})
|
||||
}
|
||||
|
||||
// send close msg and close ws connection
|
||||
defer func() {
|
||||
// 取消 WATCH CONNECT 状态
|
||||
agent.Status &= ^WATCHCONNECT
|
||||
if agent.Status == 0 {
|
||||
agent.Status = DISCONNECT
|
||||
}
|
||||
// send close msg and close ws connection
|
||||
defer func() {
|
||||
// 取消 WATCH CONNECT 状态
|
||||
agent.Status &= ^WATCHCONNECT
|
||||
if agent.Status == 0 {
|
||||
agent.Status = DISCONNECT
|
||||
}
|
||||
|
||||
agent.closeWatchConnOnce()
|
||||
agent.closeWatchConnOnce()
|
||||
|
||||
log.Infof("close watch connection, %v", agent.Hostname)
|
||||
}()
|
||||
log.Infof("close watch connection, %v", agent.Hostname)
|
||||
}()
|
||||
|
||||
// set pong handler
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
ws.SetPongHandler(func(string) error {
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
return nil
|
||||
})
|
||||
// set pong handler
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
ws.SetPongHandler(func(string) error {
|
||||
ws.SetReadDeadline(time.Now().Add(PongWait))
|
||||
return nil
|
||||
})
|
||||
|
||||
// set ping goroutine to ping every PingWait time
|
||||
go func() {
|
||||
ticker := time.NewTicker(PingWait)
|
||||
defer ticker.Stop()
|
||||
// set ping goroutine to ping every PingWait time
|
||||
go func() {
|
||||
ticker := time.NewTicker(PingWait)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if err := gs.wsping(ws, PongWait); err != nil {
|
||||
log.Errorf("watch ping to %v failed: %v", agent.Hostname, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
for range ticker.C {
|
||||
if err := gs.wsping(ws, PongWait); err != nil {
|
||||
log.Errorf("watch ping to %v failed: %v", agent.Hostname, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log.Infof("one watch agent established, %v, cmdline: %v, pid: %v, hostname: %v", ws.RemoteAddr(), agent.CmdLine, agent.Pid, agent.Hostname)
|
||||
log.Infof("one watch agent established, %v, cmdline: %v, pid: %v, hostname: %v", ws.RemoteAddr(), agent.CmdLine, agent.Pid, agent.Hostname)
|
||||
|
||||
for {
|
||||
mt, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Errorf("read from %v: %v", agent.Hostname, err)
|
||||
break
|
||||
}
|
||||
if mt == websocket.TextMessage {
|
||||
gs.watchCh <- message
|
||||
}
|
||||
}
|
||||
for {
|
||||
mt, message, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
log.Errorf("read from %v: %v", agent.Hostname, err)
|
||||
break
|
||||
}
|
||||
if mt == websocket.TextMessage {
|
||||
gs.watchCh <- message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gs *gocServer) watchLoop() {
|
||||
for {
|
||||
msg := <-gs.watchCh
|
||||
gs.watchClients.Range(func(key, value interface{}) bool {
|
||||
// 这里是客户端的 ws 连接,不是 agent ws 连接
|
||||
gwc := value.(*gocWatchClient)
|
||||
err := gwc.ws.WriteMessage(websocket.TextMessage, msg)
|
||||
if err != nil {
|
||||
gwc.ws.Close()
|
||||
gwc.once.Do(func() { close(gwc.exitCh) })
|
||||
}
|
||||
for {
|
||||
msg := <-gs.watchCh
|
||||
gs.watchClients.Range(func(key, value interface{}) bool {
|
||||
// 这里是客户端的 ws 连接,不是 agent ws 连接
|
||||
gwc := value.(*gocWatchClient)
|
||||
err := gwc.ws.WriteMessage(websocket.TextMessage, msg)
|
||||
if err != nil {
|
||||
gwc.ws.Close()
|
||||
gwc.once.Do(func() { close(gwc.exitCh) })
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -14,26 +14,26 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
|
||||
"github.com/RickLeee/goc/v2/pkg/log"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/ar0c/goc/v2/pkg/log"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func Watch(host string) {
|
||||
watchUrl := fmt.Sprintf("ws://%v/v2/cover/ws/watch", host)
|
||||
c, _, err := websocket.DefaultDialer.Dial(watchUrl, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot connect to goc server: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
watchUrl := fmt.Sprintf("ws://%v/v2/cover/ws/watch", host)
|
||||
c, _, err := websocket.DefaultDialer.Dial(watchUrl, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot connect to goc server: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
for {
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Fatalf("cannot read message: %v", err)
|
||||
}
|
||||
for {
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Fatalf("cannot read message: %v", err)
|
||||
}
|
||||
|
||||
log.Infof("profile update: %v", string(message))
|
||||
}
|
||||
log.Infof("profile update: %v", string(message))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user