commit
103dafe41d
@ -18,6 +18,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/pkg/build"
|
"github.com/qiniu/goc/pkg/build"
|
||||||
|
"github.com/qiniu/goc/pkg/cover"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,14 +46,10 @@ goc build -- -ldflags "-extldflags -static" -tags="embed kodo"
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
gocBuild := build.NewBuild(buildFlags, packages, buildOutput)
|
gocBuild := build.NewBuild(buildFlags, packages, buildOutput)
|
||||||
// remove temporary directory if needed
|
// remove temporary directory if needed
|
||||||
defer func() {
|
defer gocBuild.Clean()
|
||||||
if !debugGoc {
|
|
||||||
gocBuild.Clean()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
||||||
// in the tmp directory
|
// in the tmp directory
|
||||||
doCover(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir)
|
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center)
|
||||||
// do install in the temporary directory
|
// do install in the temporary directory
|
||||||
gocBuild.Build()
|
gocBuild.Build()
|
||||||
return
|
return
|
||||||
|
@ -13,6 +13,9 @@ var (
|
|||||||
buildFlags string
|
buildFlags string
|
||||||
packages string
|
packages string
|
||||||
appArgs string
|
appArgs string
|
||||||
|
|
||||||
|
goRunExecFlag string
|
||||||
|
goRunArguments string
|
||||||
)
|
)
|
||||||
|
|
||||||
// addBasicFlags adds a
|
// addBasicFlags adds a
|
||||||
@ -41,6 +44,8 @@ func addRunFlags(cmdset *pflag.FlagSet) {
|
|||||||
addBuildFlags(cmdset)
|
addBuildFlags(cmdset)
|
||||||
cmdset.Lookup("packages").Usage = "specify the package name, only ., ./... and *.go are supported"
|
cmdset.Lookup("packages").Usage = "specify the package name, only ., ./... and *.go are supported"
|
||||||
cmdset.StringVar(&appArgs, "appargs", "", "specify the application's arguments")
|
cmdset.StringVar(&appArgs, "appargs", "", "specify the application's arguments")
|
||||||
|
cmdset.StringVar(&goRunExecFlag, "exec", "", "same as -exec flag in 'go run' command")
|
||||||
|
cmdset.StringVar(&goRunArguments, "arguments", "", "same as 'arguments' in 'go run' command")
|
||||||
// bind to viper
|
// bind to viper
|
||||||
viper.BindPFlags(cmdset)
|
viper.BindPFlags(cmdset)
|
||||||
}
|
}
|
||||||
|
173
cmd/cover.go
173
cmd/cover.go
@ -17,14 +17,10 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qiniu/goc/pkg/cover"
|
"github.com/qiniu/goc/pkg/cover"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var coverCmd = &cobra.Command{
|
var coverCmd = &cobra.Command{
|
||||||
@ -52,7 +48,7 @@ goc cover --center=http://127.0.0.1:7777 --target=/path/to/target --mode=atomic
|
|||||||
log.Fatalf("unknown -mode %v", mode)
|
log.Fatalf("unknown -mode %v", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
doCover(buildFlags, "", target)
|
cover.Execute(buildFlags, "", target, mode, center)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,166 +57,3 @@ func init() {
|
|||||||
addCommonFlags(coverCmd.Flags())
|
addCommonFlags(coverCmd.Flags())
|
||||||
rootCmd.AddCommand(coverCmd)
|
rootCmd.AddCommand(coverCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doCover(args string, newgopath string, target string) {
|
|
||||||
if !isDirExist(target) {
|
|
||||||
log.Fatalf("target directory %s not exist", target)
|
|
||||||
}
|
|
||||||
|
|
||||||
listArgs := []string{"-json"}
|
|
||||||
if len(args) != 0 {
|
|
||||||
listArgs = append(listArgs, args)
|
|
||||||
}
|
|
||||||
listArgs = append(listArgs, "./...")
|
|
||||||
pkgs := cover.ListPackages(target, strings.Join(listArgs, " "), newgopath)
|
|
||||||
|
|
||||||
var seen = make(map[string]*cover.PackageCover)
|
|
||||||
var seenCache = make(map[string]*cover.PackageCover)
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
if pkg.Name == "main" {
|
|
||||||
log.Printf("handle package: %v", pkg.ImportPath)
|
|
||||||
// inject the main package
|
|
||||||
mainCover, err := cover.AddCounters(pkg, mode, newgopath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// new a testcover for this service
|
|
||||||
tc := cover.TestCover{
|
|
||||||
Mode: mode,
|
|
||||||
Center: center,
|
|
||||||
MainPkgCover: mainCover,
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle its dependency
|
|
||||||
var internalPkgCache = make(map[string][]*cover.PackageCover)
|
|
||||||
tc.CacheCover = make(map[string]*cover.PackageCover)
|
|
||||||
for _, dep := range pkg.Deps {
|
|
||||||
|
|
||||||
if packageCover, ok := seen[dep]; ok {
|
|
||||||
tc.DepsCover = append(tc.DepsCover, packageCover)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//only focus package neither standard Go library nor dependency library
|
|
||||||
if depPkg, ok := pkgs[dep]; ok {
|
|
||||||
|
|
||||||
if findInternal(dep) {
|
|
||||||
|
|
||||||
//scan exist cache cover to tc.CacheCover
|
|
||||||
if cache, ok := seenCache[dep]; ok {
|
|
||||||
log.Printf("cache cover exist: %s", cache.Package.ImportPath)
|
|
||||||
tc.CacheCover[cache.Package.Dir] = cache
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// add counter for internal package
|
|
||||||
inPkgCover, err := cover.AddCounters(depPkg, mode, newgopath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err)
|
|
||||||
}
|
|
||||||
parentDir := getInternalParent(depPkg.Dir)
|
|
||||||
parentImportPath := getInternalParent(depPkg.ImportPath)
|
|
||||||
|
|
||||||
//if internal parent dir or import is root path, ignore the dep. the dep is Go library nor dependency library
|
|
||||||
if parentDir == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if parentImportPath == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg := &cover.Package{
|
|
||||||
ImportPath: parentImportPath,
|
|
||||||
Dir: parentDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some internal package have same parent dir or import path
|
|
||||||
// Cache all vars by internal parent dir for all child internal counter vars
|
|
||||||
cacheCover := cover.AddCacheCover(pkg, inPkgCover)
|
|
||||||
if v, ok := tc.CacheCover[cacheCover.Package.Dir]; ok {
|
|
||||||
for cVar, val := range v.Vars {
|
|
||||||
cacheCover.Vars[cVar] = val
|
|
||||||
}
|
|
||||||
tc.CacheCover[cacheCover.Package.Dir] = cacheCover
|
|
||||||
} else {
|
|
||||||
tc.CacheCover[cacheCover.Package.Dir] = cacheCover
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache all internal vars to internal parent package
|
|
||||||
inCover := cover.CacheInternalCover(inPkgCover)
|
|
||||||
if v, ok := internalPkgCache[cacheCover.Package.Dir]; ok {
|
|
||||||
v = append(v, inCover)
|
|
||||||
internalPkgCache[cacheCover.Package.Dir] = v
|
|
||||||
} else {
|
|
||||||
var covers []*cover.PackageCover
|
|
||||||
covers = append(covers, inCover)
|
|
||||||
internalPkgCache[cacheCover.Package.Dir] = covers
|
|
||||||
}
|
|
||||||
seenCache[dep] = cacheCover
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
packageCover, err := cover.AddCounters(depPkg, mode, newgopath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err)
|
|
||||||
}
|
|
||||||
tc.DepsCover = append(tc.DepsCover, packageCover)
|
|
||||||
seen[dep] = packageCover
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errs := cover.InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 {
|
|
||||||
log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inject Http Cover APIs
|
|
||||||
var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir)
|
|
||||||
if err := cover.InjectCountersHandlers(tc, httpCoverApis); err != nil {
|
|
||||||
log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDirExist(path string) bool {
|
|
||||||
s, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return s.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refer: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L1334:6
|
|
||||||
// findInternal looks for the final "internal" path element in the given import path.
|
|
||||||
// If there isn't one, findInternal returns ok=false.
|
|
||||||
// Otherwise, findInternal returns ok=true and the index of the "internal".
|
|
||||||
func findInternal(path string) bool {
|
|
||||||
// Three cases, depending on internal at start/end of string or not.
|
|
||||||
// The order matters: we must return the index of the final element,
|
|
||||||
// because the final one produces the most restrictive requirement
|
|
||||||
// on the importer.
|
|
||||||
switch {
|
|
||||||
case strings.HasSuffix(path, "/internal"):
|
|
||||||
return true
|
|
||||||
case strings.Contains(path, "/internal/"):
|
|
||||||
return true
|
|
||||||
case path == "internal", strings.HasPrefix(path, "internal/"):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getInternalParent(path string) string {
|
|
||||||
switch {
|
|
||||||
case strings.HasSuffix(path, "/internal"):
|
|
||||||
return strings.Split(path, "/internal")[0]
|
|
||||||
case strings.Contains(path, "/internal/"):
|
|
||||||
return strings.Split(path, "/internal/")[0]
|
|
||||||
case path == "internal":
|
|
||||||
return ""
|
|
||||||
case strings.HasPrefix(path, "internal/"):
|
|
||||||
return strings.Split(path, "internal/")[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
@ -18,6 +18,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/qiniu/goc/pkg/build"
|
"github.com/qiniu/goc/pkg/build"
|
||||||
|
"github.com/qiniu/goc/pkg/cover"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,14 +43,10 @@ goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'"
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
gocBuild := build.NewInstall(buildFlags, packages)
|
gocBuild := build.NewInstall(buildFlags, packages)
|
||||||
// remove temporary directory if needed
|
// remove temporary directory if needed
|
||||||
defer func() {
|
defer gocBuild.Clean()
|
||||||
if !debugGoc {
|
|
||||||
gocBuild.Clean()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
// doCover with original buildFlags, with new GOPATH( tmp:original )
|
||||||
// in the tmp directory
|
// in the tmp directory
|
||||||
doCover(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir)
|
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center)
|
||||||
// do install in the temporary directory
|
// do install in the temporary directory
|
||||||
gocBuild.Install()
|
gocBuild.Install()
|
||||||
},
|
},
|
||||||
|
73
cmd/run.go
Normal file
73
cmd/run.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 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 (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/qiniu/goc/pkg/build"
|
||||||
|
"github.com/qiniu/goc/pkg/cover"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var runCmd = &cobra.Command{
|
||||||
|
Use: "run",
|
||||||
|
Short: "Run covers and runs the named main Go package",
|
||||||
|
Long: `Run covers and runs the named main Go package,
|
||||||
|
It is exactly behave as 'go run .' in addition of some internal goc features.`,
|
||||||
|
Example: `
|
||||||
|
goc run .
|
||||||
|
`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
gocBuild := build.NewBuild(buildFlags, packages, buildOutput)
|
||||||
|
gocBuild.GoRunExecFlag = goRunExecFlag
|
||||||
|
gocBuild.GoRunArguments = goRunArguments
|
||||||
|
defer gocBuild.Clean()
|
||||||
|
|
||||||
|
// only save services in memory
|
||||||
|
cover.DefaultStore = cover.NewMemoryStore()
|
||||||
|
|
||||||
|
// start goc server
|
||||||
|
var l = newLocalListener()
|
||||||
|
go cover.GocServer(ioutil.Discard).RunListener(l)
|
||||||
|
gocServer := fmt.Sprintf("http://%s", l.Addr().String())
|
||||||
|
fmt.Printf("[goc] goc server started: %s \n", gocServer)
|
||||||
|
|
||||||
|
// execute covers for the target source with original buildFlags and new GOPATH( tmp:original )
|
||||||
|
cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, gocServer)
|
||||||
|
|
||||||
|
gocBuild.Run()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
addRunFlags(runCmd.Flags())
|
||||||
|
rootCmd.AddCommand(runCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLocalListener() net.Listener {
|
||||||
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
|
||||||
|
log.Fatalf("failed to listen on a port: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
@ -35,10 +35,17 @@ type Build struct {
|
|||||||
TmpDir string // the temporary directory to build the project
|
TmpDir string // the temporary directory to build the project
|
||||||
TmpWorkingDir string // the working directory in the temporary directory, which is corresponding to the current directory in the project directory
|
TmpWorkingDir string // the working directory in the temporary directory, which is corresponding to the current directory in the project directory
|
||||||
IsMod bool // determine whether it is a Mod project
|
IsMod bool // determine whether it is a Mod project
|
||||||
BuildFlags string // Build flags
|
|
||||||
Packages string // Packages that needs to build
|
|
||||||
Root string // Project Root
|
Root string // Project Root
|
||||||
Target string // the binary name that go build generate
|
Target string // the binary name that go build generate
|
||||||
|
|
||||||
|
// keep compatible with go commands:
|
||||||
|
// go run [build flags] [-exec xprog] package [arguments...]
|
||||||
|
// go build [-o output] [-i] [build flags] [packages]
|
||||||
|
// go install [-i] [build flags] [packages]
|
||||||
|
BuildFlags string // Build flags
|
||||||
|
Packages string // Packages that needs to build
|
||||||
|
GoRunExecFlag string // for the -exec flags in go run command
|
||||||
|
GoRunArguments string // for the '[arguments]' parameters in go run command
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuild creates a Build struct which can build from goc temporary directory,
|
// NewBuild creates a Build struct which can build from goc temporary directory,
|
||||||
@ -108,7 +115,30 @@ func (b *Build) determineOutputDir(outputDir string) string {
|
|||||||
func (b *Build) validatePackageForBuild() bool {
|
func (b *Build) validatePackageForBuild() bool {
|
||||||
if b.Packages == "." {
|
if b.Packages == "." {
|
||||||
return true
|
return true
|
||||||
} else {
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run excutes the main package in addition with the internal goc features
|
||||||
|
func (b *Build) Run() {
|
||||||
|
cmd := exec.Command("/bin/bash", "-c", "go run "+b.BuildFlags+" "+b.GoRunExecFlag+" "+b.Packages+" "+b.GoRunArguments)
|
||||||
|
cmd.Dir = b.TmpWorkingDir
|
||||||
|
|
||||||
|
if b.NewGOPATH != "" {
|
||||||
|
// Change to temp GOPATH for go install command
|
||||||
|
cmd.Env = append(os.Environ(), fmt.Sprintf("GOPATH=%v", b.NewGOPATH))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("go build cmd is: %v", cmd.Args)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Fail to start command: %v. The error is: %v", cmd.Args, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Wait(); err != nil {
|
||||||
|
log.Fatalf("Fail to execute command: %v. The error is: %v", cmd.Args, err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,8 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/otiai10/copy"
|
"github.com/otiai10/copy"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Build) cpGoModulesProject() {
|
func (b *Build) cpGoModulesProject() {
|
||||||
|
@ -18,9 +18,10 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewInstall creates a Build struct which can install from goc temporary directory
|
// NewInstall creates a Build struct which can install from goc temporary directory
|
||||||
@ -59,7 +60,6 @@ func (b *Build) Install() {
|
|||||||
func (b *Build) validatePackageForInstall() bool {
|
func (b *Build) validatePackageForInstall() bool {
|
||||||
if b.Packages == "." || b.Packages == "./..." {
|
if b.Packages == "." || b.Packages == "./..." {
|
||||||
return true
|
return true
|
||||||
} else {
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/otiai10/copy"
|
"github.com/otiai10/copy"
|
||||||
"github.com/qiniu/goc/pkg/cover"
|
"github.com/qiniu/goc/pkg/cover"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Build) cpLegacyProject() {
|
func (b *Build) cpLegacyProject() {
|
||||||
|
@ -23,9 +23,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/qiniu/goc/pkg/cover"
|
"github.com/qiniu/goc/pkg/cover"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Build) MvProjectsToTmp() {
|
func (b *Build) MvProjectsToTmp() {
|
||||||
@ -156,5 +156,8 @@ func (b *Build) findWhereToInstall() string {
|
|||||||
|
|
||||||
// Clean clears up the temporary workspace
|
// Clean clears up the temporary workspace
|
||||||
func (b *Build) Clean() error {
|
func (b *Build) Clean() error {
|
||||||
|
if !viper.GetBool("debug") {
|
||||||
return os.RemoveAll(b.TmpDir)
|
return os.RemoveAll(b.TmpDir)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -33,6 +32,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -112,6 +113,125 @@ type PackageError struct {
|
|||||||
Err string // the error itself
|
Err string // the error itself
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Execute execute go tool cover for all the .go files in the target folder
|
||||||
|
func Execute(args, newGopath, target, mode, center string) {
|
||||||
|
if !isDirExist(target) {
|
||||||
|
log.Fatalf("target directory %s not exist", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
listArgs := []string{"-json"}
|
||||||
|
if len(args) != 0 {
|
||||||
|
listArgs = append(listArgs, args)
|
||||||
|
}
|
||||||
|
listArgs = append(listArgs, "./...")
|
||||||
|
pkgs := ListPackages(target, strings.Join(listArgs, " "), newGopath)
|
||||||
|
|
||||||
|
var seen = make(map[string]*PackageCover)
|
||||||
|
var seenCache = make(map[string]*PackageCover)
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if pkg.Name == "main" {
|
||||||
|
log.Printf("handle package: %v", pkg.ImportPath)
|
||||||
|
// inject the main package
|
||||||
|
mainCover, err := AddCounters(pkg, mode, newGopath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// new a testcover for this service
|
||||||
|
tc := TestCover{
|
||||||
|
Mode: mode,
|
||||||
|
Center: center,
|
||||||
|
MainPkgCover: mainCover,
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle its dependency
|
||||||
|
var internalPkgCache = make(map[string][]*PackageCover)
|
||||||
|
tc.CacheCover = make(map[string]*PackageCover)
|
||||||
|
for _, dep := range pkg.Deps {
|
||||||
|
if packageCover, ok := seen[dep]; ok {
|
||||||
|
tc.DepsCover = append(tc.DepsCover, packageCover)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//only focus package neither standard Go library nor dependency library
|
||||||
|
if depPkg, ok := pkgs[dep]; ok {
|
||||||
|
if hasInternalPath(dep) {
|
||||||
|
//scan exist cache cover to tc.CacheCover
|
||||||
|
if cache, ok := seenCache[dep]; ok {
|
||||||
|
log.Printf("cache cover exist: %s", cache.Package.ImportPath)
|
||||||
|
tc.CacheCover[cache.Package.Dir] = cache
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// add counter for internal package
|
||||||
|
inPkgCover, err := AddCounters(depPkg, mode, newGopath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err)
|
||||||
|
}
|
||||||
|
parentDir := getInternalParent(depPkg.Dir)
|
||||||
|
parentImportPath := getInternalParent(depPkg.ImportPath)
|
||||||
|
|
||||||
|
//if internal parent dir or import is root path, ignore the dep. the dep is Go library nor dependency library
|
||||||
|
if parentDir == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parentImportPath == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := &Package{
|
||||||
|
ImportPath: parentImportPath,
|
||||||
|
Dir: parentDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some internal package have same parent dir or import path
|
||||||
|
// Cache all vars by internal parent dir for all child internal counter vars
|
||||||
|
cacheCover := AddCacheCover(pkg, inPkgCover)
|
||||||
|
if v, ok := tc.CacheCover[cacheCover.Package.Dir]; ok {
|
||||||
|
for cVar, val := range v.Vars {
|
||||||
|
cacheCover.Vars[cVar] = val
|
||||||
|
}
|
||||||
|
tc.CacheCover[cacheCover.Package.Dir] = cacheCover
|
||||||
|
} else {
|
||||||
|
tc.CacheCover[cacheCover.Package.Dir] = cacheCover
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache all internal vars to internal parent package
|
||||||
|
inCover := CacheInternalCover(inPkgCover)
|
||||||
|
if v, ok := internalPkgCache[cacheCover.Package.Dir]; ok {
|
||||||
|
v = append(v, inCover)
|
||||||
|
internalPkgCache[cacheCover.Package.Dir] = v
|
||||||
|
} else {
|
||||||
|
var covers []*PackageCover
|
||||||
|
covers = append(covers, inCover)
|
||||||
|
internalPkgCache[cacheCover.Package.Dir] = covers
|
||||||
|
}
|
||||||
|
seenCache[dep] = cacheCover
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
packageCover, err := AddCounters(depPkg, mode, newGopath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err)
|
||||||
|
}
|
||||||
|
tc.DepsCover = append(tc.DepsCover, packageCover)
|
||||||
|
seen[dep] = packageCover
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs := InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 {
|
||||||
|
log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// inject Http Cover APIs
|
||||||
|
var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir)
|
||||||
|
if err := InjectCountersHandlers(tc, httpCoverApis); err != nil {
|
||||||
|
log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ListPackages list all packages under specific via go list command
|
// ListPackages list all packages under specific via go list command
|
||||||
// The argument newgopath is if you need to go list in a different GOPATH
|
// The argument newgopath is if you need to go list in a different GOPATH
|
||||||
func ListPackages(dir string, args string, newgopath string) map[string]*Package {
|
func ListPackages(dir string, args string, newgopath string) map[string]*Package {
|
||||||
@ -167,6 +287,48 @@ func AddCounters(pkg *Package, mode, newgopath string) (*PackageCover, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDirExist(path string) bool {
|
||||||
|
s, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refer: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L1334:6
|
||||||
|
// hasInternalPath looks for the final "internal" path element in the given import path.
|
||||||
|
// If there isn't one, hasInternalPath returns ok=false.
|
||||||
|
// Otherwise, hasInternalPath returns ok=true and the index of the "internal".
|
||||||
|
func hasInternalPath(path string) bool {
|
||||||
|
// Three cases, depending on internal at start/end of string or not.
|
||||||
|
// The order matters: we must return the index of the final element,
|
||||||
|
// because the final one produces the most restrictive requirement
|
||||||
|
// on the importer.
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(path, "/internal"):
|
||||||
|
return true
|
||||||
|
case strings.Contains(path, "/internal/"):
|
||||||
|
return true
|
||||||
|
case path == "internal", strings.HasPrefix(path, "internal/"):
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInternalParent(path string) string {
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(path, "/internal"):
|
||||||
|
return strings.Split(path, "/internal")[0]
|
||||||
|
case strings.Contains(path, "/internal/"):
|
||||||
|
return strings.Split(path, "/internal/")[0]
|
||||||
|
case path == "internal":
|
||||||
|
return ""
|
||||||
|
case strings.HasPrefix(path, "internal/"):
|
||||||
|
return strings.Split(path, "internal/")[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func buildCoverCmd(file string, coverVar *FileVar, pkg *Package, mode, newgopath string) *exec.Cmd {
|
func buildCoverCmd(file string, coverVar *FileVar, pkg *Package, mode, newgopath string) *exec.Cmd {
|
||||||
// to construct: go tool cover -mode=atomic -o dest src (note: dest==src)
|
// to construct: go tool cover -mode=atomic -o dest src (note: dest==src)
|
||||||
var newArgs = []string{"tool", "cover"}
|
var newArgs = []string{"tool", "cover"}
|
||||||
|
@ -18,7 +18,6 @@ package cover
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -26,6 +25,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -225,3 +226,81 @@ func TestDeclareCoverVars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetInternalParent(t *testing.T) {
|
||||||
|
var tcs = []struct {
|
||||||
|
ImportPath string
|
||||||
|
expectedParent string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ImportPath: "a/internal/b",
|
||||||
|
expectedParent: "a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "internal/b",
|
||||||
|
expectedParent: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "a/b/internal/b",
|
||||||
|
expectedParent: "a/b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "a/b/internal",
|
||||||
|
expectedParent: "a/b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "a/b/internal/c",
|
||||||
|
expectedParent: "a/b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "a/b/c",
|
||||||
|
expectedParent: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "",
|
||||||
|
expectedParent: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tcs {
|
||||||
|
actual := getInternalParent(tc.ImportPath)
|
||||||
|
if actual != tc.expectedParent {
|
||||||
|
t.Errorf("getInternalParent failed for importPath %s, expected %s, got %s", tc.ImportPath, tc.expectedParent, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindInternal(t *testing.T) {
|
||||||
|
var tcs = []struct {
|
||||||
|
ImportPath string
|
||||||
|
expectedParent bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ImportPath: "a/internal/b",
|
||||||
|
expectedParent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "internal/b",
|
||||||
|
expectedParent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "a/b/internal",
|
||||||
|
expectedParent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "a/b/c",
|
||||||
|
expectedParent: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ImportPath: "internal",
|
||||||
|
expectedParent: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tcs {
|
||||||
|
actual := hasInternalPath(tc.ImportPath)
|
||||||
|
if actual != tc.expectedParent {
|
||||||
|
t.Errorf("hasInternalPath check failed for importPath %s", tc.ImportPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -145,9 +145,10 @@ func clearFileCover(counter []uint32) {
|
|||||||
func registerHandlers() {
|
func registerHandlers() {
|
||||||
ln, host, err := listen()
|
ln, host, err := listen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("profile listen failed, err:%v", err)
|
log.Fatalf("listen failed, err:%v", err)
|
||||||
}
|
}
|
||||||
log.Println("profile listen on", host)
|
|
||||||
|
fmt.Printf("[goc] goc agent listened on: %s \n", host)
|
||||||
profileAddr := "http://" + host
|
profileAddr := "http://" + host
|
||||||
if resp, err := registerSelf(profileAddr); err != nil {
|
if resp, err := registerSelf(profileAddr); err != nil {
|
||||||
log.Fatalf("register address %v failed, err: %v, response: %v", profileAddr, err, string(resp))
|
log.Fatalf("register address %v failed, err: %v, response: %v", profileAddr, err, string(resp))
|
||||||
@ -220,7 +221,7 @@ func registerSelf(address string) ([]byte, error) {
|
|||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil && isNetworkError(err) {
|
if err != nil && isNetworkError(err) {
|
||||||
log.Printf("[WARN]error occured:%v, try again", err)
|
log.Printf("[goc][WARN]error occured:%v, try again", err)
|
||||||
resp, err = http.DefaultClient.Do(req)
|
resp, err = http.DefaultClient.Do(req)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
@ -19,7 +19,6 @@ package cover
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
@ -28,18 +27,19 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/tools/cover"
|
"golang.org/x/tools/cover"
|
||||||
"k8s.io/test-infra/gopherage/pkg/cov"
|
"k8s.io/test-infra/gopherage/pkg/cov"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LocalStore implements the IPersistence interface
|
// DefaultStore implements the IPersistence interface
|
||||||
var LocalStore Store
|
var DefaultStore Store
|
||||||
|
|
||||||
// LogFile a file to save log.
|
// LogFile a file to save log.
|
||||||
const LogFile = "goc.log"
|
const LogFile = "goc.log"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
LocalStore = NewStore()
|
DefaultStore = NewFileStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts coverage host center
|
// Run starts coverage host center
|
||||||
@ -48,16 +48,18 @@ func Run(port string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to create log file %s, err: %v", LogFile, err)
|
log.Fatalf("failed to create log file %s, err: %v", LogFile, err)
|
||||||
}
|
}
|
||||||
r := GocServer(f)
|
|
||||||
|
// both log to stdout and file by default
|
||||||
|
mw := io.MultiWriter(f, os.Stdout)
|
||||||
|
r := GocServer(mw)
|
||||||
log.Fatal(r.Run(port))
|
log.Fatal(r.Run(port))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GocServer init goc server engine
|
// GocServer init goc server engine
|
||||||
func GocServer(w io.Writer) *gin.Engine {
|
func GocServer(w io.Writer) *gin.Engine {
|
||||||
if w != nil && w != os.Stdout {
|
if w != nil {
|
||||||
gin.DefaultWriter = io.MultiWriter(w, os.Stdout)
|
gin.DefaultWriter = w
|
||||||
}
|
}
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
// api to show the registered services
|
// api to show the registered services
|
||||||
r.StaticFile(PersistenceFile, "./"+PersistenceFile)
|
r.StaticFile(PersistenceFile, "./"+PersistenceFile)
|
||||||
@ -82,7 +84,7 @@ type Service struct {
|
|||||||
|
|
||||||
//listServices list all the registered services
|
//listServices list all the registered services
|
||||||
func listServices(c *gin.Context) {
|
func listServices(c *gin.Context) {
|
||||||
services := LocalStore.GetAll()
|
services := DefaultStore.GetAll()
|
||||||
c.JSON(http.StatusOK, services)
|
c.JSON(http.StatusOK, services)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +111,7 @@ func registerService(c *gin.Context) {
|
|||||||
log.Printf("the registed host %s of service %s is different with the real one %s, here we choose the real one", service.Name, host, realIP)
|
log.Printf("the registed host %s of service %s is different with the real one %s, here we choose the real one", service.Name, host, realIP)
|
||||||
service.Address = fmt.Sprintf("http://%s:%s", realIP, port)
|
service.Address = fmt.Sprintf("http://%s:%s", realIP, port)
|
||||||
}
|
}
|
||||||
if err := LocalStore.Add(service); err != nil {
|
if err := DefaultStore.Add(service); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -118,7 +120,7 @@ func registerService(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func profile(c *gin.Context) {
|
func profile(c *gin.Context) {
|
||||||
svrsUnderTest := LocalStore.GetAll()
|
svrsUnderTest := DefaultStore.GetAll()
|
||||||
var mergedProfiles = make([][]*cover.Profile, len(svrsUnderTest))
|
var mergedProfiles = make([][]*cover.Profile, len(svrsUnderTest))
|
||||||
for _, addrs := range svrsUnderTest {
|
for _, addrs := range svrsUnderTest {
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
@ -154,7 +156,7 @@ func profile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func clear(c *gin.Context) {
|
func clear(c *gin.Context) {
|
||||||
svrsUnderTest := LocalStore.GetAll()
|
svrsUnderTest := DefaultStore.GetAll()
|
||||||
for svc, addrs := range svrsUnderTest {
|
for svc, addrs := range svrsUnderTest {
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
pp, err := NewWorker(addr).Clear()
|
pp, err := NewWorker(addr).Clear()
|
||||||
@ -168,7 +170,7 @@ func clear(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initSystem(c *gin.Context) {
|
func initSystem(c *gin.Context) {
|
||||||
if err := LocalStore.Init(); err != nil {
|
if err := DefaultStore.Init(); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,11 @@ package cover
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store persistents the registered service information
|
// Store persistents the registered service information
|
||||||
@ -38,77 +39,82 @@ type Store interface {
|
|||||||
|
|
||||||
// Init cleanup all the registered service information
|
// Init cleanup all the registered service information
|
||||||
Init() error
|
Init() error
|
||||||
|
|
||||||
|
// Set stores the services information into internal state
|
||||||
|
Set(services map[string][]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PersistenceFile is the file to save services address information
|
// PersistenceFile is the file to save services address information
|
||||||
const PersistenceFile = "_svrs_address.txt"
|
const PersistenceFile = "_svrs_address.txt"
|
||||||
|
|
||||||
// localStore holds the registered services into memory and persistent to a local file
|
// fileStore holds the registered services into memory and persistent to a local file
|
||||||
type localStore struct {
|
type fileStore struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
servicesMap map[string][]string
|
|
||||||
persistentFile string
|
persistentFile string
|
||||||
|
|
||||||
|
memoryStore Store
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds the given service to localStore
|
// NewFileStore creates a store using local file
|
||||||
func (l *localStore) Add(s Service) error {
|
func NewFileStore() Store {
|
||||||
l.mu.Lock()
|
l := &fileStore{
|
||||||
defer l.mu.Unlock()
|
persistentFile: PersistenceFile,
|
||||||
// load to memory
|
memoryStore: NewMemoryStore(),
|
||||||
if addrs, ok := l.servicesMap[s.Name]; ok {
|
|
||||||
for _, addr := range addrs {
|
|
||||||
if addr == s.Address {
|
|
||||||
log.Printf("service registered already, name: %s, address: %s", s.Name, s.Address)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := l.load(); err != nil {
|
||||||
|
log.Fatalf("load failed, file: %s, err: %v", l.persistentFile, err)
|
||||||
}
|
}
|
||||||
addrs = append(addrs, s.Address)
|
|
||||||
l.servicesMap[s.Name] = addrs
|
return l
|
||||||
} else {
|
|
||||||
l.servicesMap[s.Name] = []string{s.Address}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add adds the given service to file Store
|
||||||
|
func (l *fileStore) Add(s Service) error {
|
||||||
|
l.memoryStore.Add(s)
|
||||||
|
|
||||||
// persistent to local store
|
// persistent to local store
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
return l.appendToFile(s)
|
return l.appendToFile(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the registered service information with the given name
|
// Get returns the registered service information with the given name
|
||||||
func (l *localStore) Get(name string) []string {
|
func (l *fileStore) Get(name string) []string {
|
||||||
l.mu.RLock()
|
return l.memoryStore.Get(name)
|
||||||
defer l.mu.RUnlock()
|
|
||||||
return l.servicesMap[name]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns all the registered service information
|
// Get returns all the registered service information
|
||||||
func (l *localStore) GetAll() map[string][]string {
|
func (l *fileStore) GetAll() map[string][]string {
|
||||||
l.mu.RLock()
|
return l.memoryStore.GetAll()
|
||||||
defer l.mu.RUnlock()
|
|
||||||
return l.servicesMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init cleanup all the registered service information
|
// Init cleanup all the registered service information
|
||||||
// and the local persistent file
|
// and the local persistent file
|
||||||
func (l *localStore) Init() error {
|
func (l *fileStore) Init() error {
|
||||||
|
if err := l.memoryStore.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
if err := os.Remove(l.persistentFile); err != nil && !os.IsNotExist(err) {
|
if err := os.Remove(l.persistentFile); err != nil && !os.IsNotExist(err) {
|
||||||
return fmt.Errorf("failed to delete file %s, err: %v", l.persistentFile, err)
|
return fmt.Errorf("failed to delete file %s, err: %v", l.persistentFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.servicesMap = make(map[string][]string, 0)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// load all registered service from file to memory
|
// load all registered service from file to memory
|
||||||
func (l *localStore) load() (map[string][]string, error) {
|
func (l *fileStore) load() error {
|
||||||
var svrsMap = make(map[string][]string, 0)
|
var svrsMap = make(map[string][]string, 0)
|
||||||
|
|
||||||
f, err := os.Open(l.persistentFile)
|
f, err := os.Open(l.persistentFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return svrsMap, nil
|
return nil
|
||||||
}
|
}
|
||||||
return svrsMap, fmt.Errorf("failed to open file, path: %s, err: %v", l.persistentFile, err)
|
return fmt.Errorf("failed to open file, path: %s, err: %v", l.persistentFile, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
@ -129,13 +135,19 @@ func (l *localStore) load() (map[string][]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := ns.Err(); err != nil {
|
if err := ns.Err(); err != nil {
|
||||||
return svrsMap, fmt.Errorf("read file failed, file: %s, err: %v", l.persistentFile, err)
|
return fmt.Errorf("read file failed, file: %s, err: %v", l.persistentFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return svrsMap, nil
|
// set information to memory
|
||||||
|
l.memoryStore.Set(svrsMap)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *localStore) appendToFile(s Service) error {
|
func (l *fileStore) Set(services map[string][]string) {
|
||||||
|
panic("TO BE IMPLEMENTED")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *fileStore) appendToFile(s Service) error {
|
||||||
f, err := os.OpenFile(l.persistentFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
f, err := os.OpenFile(l.persistentFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -159,17 +171,67 @@ func split(r rune) bool {
|
|||||||
return r == '&'
|
return r == '&'
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStore creates a store using local file
|
// memoryStore holds the registered services only into memory
|
||||||
func NewStore() Store {
|
type memoryStore struct {
|
||||||
l := &localStore{
|
mu sync.RWMutex
|
||||||
persistentFile: PersistenceFile,
|
servicesMap map[string][]string
|
||||||
servicesMap: make(map[string][]string, 0),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
services, err := l.load()
|
// NewMemoryStore creates a memory store
|
||||||
if err != nil {
|
func NewMemoryStore() Store {
|
||||||
log.Fatalf("load failed, file: %s, err: %v", l.persistentFile, err)
|
return &memoryStore{
|
||||||
|
servicesMap: make(map[string][]string, 0),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the given service to MemoryStore
|
||||||
|
func (l *memoryStore) Add(s Service) error {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
// load to memory
|
||||||
|
if addrs, ok := l.servicesMap[s.Name]; ok {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr == s.Address {
|
||||||
|
log.Printf("service registered already, name: %s, address: %s", s.Name, s.Address)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addrs = append(addrs, s.Address)
|
||||||
|
l.servicesMap[s.Name] = addrs
|
||||||
|
} else {
|
||||||
|
l.servicesMap[s.Name] = []string{s.Address}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the registered service information with the given name
|
||||||
|
func (l *memoryStore) Get(name string) []string {
|
||||||
|
l.mu.RLock()
|
||||||
|
defer l.mu.RUnlock()
|
||||||
|
return l.servicesMap[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns all the registered service information
|
||||||
|
func (l *memoryStore) GetAll() map[string][]string {
|
||||||
|
l.mu.RLock()
|
||||||
|
defer l.mu.RUnlock()
|
||||||
|
return l.servicesMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init cleanup all the registered service information
|
||||||
|
// and the local persistent file
|
||||||
|
func (l *memoryStore) Init() error {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
l.servicesMap = make(map[string][]string, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *memoryStore) Set(services map[string][]string) {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.servicesMap = services
|
l.servicesMap = services
|
||||||
return l
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestLocalStore(t *testing.T) {
|
func TestLocalStore(t *testing.T) {
|
||||||
localStore := NewStore()
|
localStore := NewFileStore()
|
||||||
var tc1 = Service{
|
var tc1 = Service{
|
||||||
Name: "a",
|
Name: "a",
|
||||||
Address: "http://127.0.0.1",
|
Address: "http://127.0.0.1",
|
||||||
@ -44,7 +44,7 @@ func TestLocalStore(t *testing.T) {
|
|||||||
localStore.Add(tc4)
|
localStore.Add(tc4)
|
||||||
addrs := localStore.Get(tc1.Name)
|
addrs := localStore.Get(tc1.Name)
|
||||||
if len(addrs) != 2 {
|
if len(addrs) != 2 {
|
||||||
t.Error("unexpect result")
|
t.Error("unexpected result")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
|
Loading…
Reference in New Issue
Block a user