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