├── testutils ├── testutil_test.go ├── helper │ ├── docker │ │ └── Dockerfile │ ├── helper_suite_test.go │ ├── http_echo.go │ └── testrunner.go ├── runners │ └── ports.go ├── testutils_suite_test.go ├── logs.go ├── make.go ├── skip.go ├── goimpl │ ├── goimpl_suite_test.go │ ├── curl_test.go │ └── curl.go ├── random.go ├── get_current_file_test.go ├── get_current_file.go ├── port │ └── port.go ├── README.md ├── deploy_from_yaml.go ├── clusterlock │ └── clusterlock_suite_test.go ├── assertions.go ├── deploy_testrunner.go ├── exec │ └── cmd.go ├── testutil.go ├── fail_handler.go ├── kube │ ├── kube_create.go │ └── istio_teardown.go └── trimmed_stack_fail_handler.go ├── wmanager ├── go.mod └── README.md ├── grpcx ├── logo.png ├── go.mod ├── README.md ├── limiters.go └── balancer.go ├── shell ├── logo.png ├── go.mod ├── go.sum ├── yum.go └── README.md ├── healthutils ├── interface.go ├── healthchecker_suite_test.go └── grpc.go ├── dockerutils ├── save.go ├── docker_suite_test.go ├── exec.go ├── pull.go └── docker_test.go ├── logutils ├── logger.go ├── raw_logger_test.go ├── json_logger.go ├── raw_logger.go ├── log_s.go ├── dummylogger.go ├── json_logger_test.go ├── log_test.go ├── severity.go ├── log.go ├── defaultlogger.go ├── print_test.go └── internal_logger.go ├── versionutils ├── dep │ └── types.go ├── kubeapi │ ├── kubeapi_suite_test.go │ ├── list.go │ └── list_test.go ├── versionutils_suite_test.go └── cli.go ├── configutils ├── test │ └── config_suite_test.go ├── config_map_client_mock.go ├── README.md └── config_map_client.go ├── vfsutils ├── vfs_suite_test.go ├── vfs_test.go ├── mount_test.go └── afero.go ├── certutils ├── certutils_suite_test.go └── gen_cert_test.go ├── kubectrlutils ├── kubeutils_suite_test.go ├── pod_termination.go ├── wait_crd.go ├── namespaces.go ├── wait_crd_test.go ├── util.go ├── kube_cfg.go └── util_test.go ├── kubesetuputils ├── shared_suite_test.go ├── kube_errs.go ├── crd.go └── install_kube_manifest_test.go ├── nameutils ├── nameutils_suite_test.go ├── sanitize_name.go └── sanitize_name_test.go ├── slackutils ├── slack_suite_test.go ├── http.go └── slack.go ├── contextutils ├── time.go └── errors_and_logging.go ├── botutils ├── consts.go ├── botconfig │ ├── botconfig_suite_test.go │ └── os_mock_test.go ├── README.md ├── interface.go └── server.go ├── githubutils ├── githubutils_suite_test.go └── README.md ├── pkgmgmtutils └── pkgmgmtutils_suite_test.go ├── surveyutils ├── surveyutils_suite_test.go └── input_test.go ├── kubeinstallutils ├── helmchart │ ├── helmchart_suite_test.go │ └── docs_test.go ├── installutils_suite_test.go ├── kubeinstall │ ├── kubeinstall_suite_test.go │ ├── mocks │ │ └── mock_kube_installer.go │ └── installer_callbacks.go ├── kuberesource │ ├── kuberesource_suite_test.go │ └── get_cluster_resources_test.go ├── manifests_test.go ├── test │ └── inputs │ │ └── input_manifest.go └── manifests.go ├── .gitignore ├── protoutils └── protoutil_suite_test.go ├── funcutils ├── rand.go └── retry.go ├── errutils ├── aggregate_errs.go ├── errors.go └── errors_test.go ├── fileutils ├── file_crud_test.go ├── messages.go ├── file_crud.go ├── messages_test.go ├── writter.go └── tar_test.go ├── strutils ├── regex.go ├── hash.go ├── text.go ├── strings.go ├── url.go ├── colorstring_test.go ├── regex_test.go └── freezable.go ├── .vscode └── launch.json ├── osutils └── client.go ├── manifestutils ├── test │ └── example_suite_test.go └── README.md ├── progressutils ├── wrapper_test.go ├── spinner_test.go ├── progress.go ├── wrapper.go ├── spinner.go └── progress_test.go ├── commandutils ├── git │ └── git.go └── command_test.go ├── pkcs12utils ├── internal │ └── rc2 │ │ ├── bench_test.go │ │ └── rc2_test.go ├── errors.go ├── README.md ├── pbkdf_test.go ├── bmp-string.go ├── mac.go ├── bmp-string_test.go └── mac_test.go ├── maputils ├── ordered_map_iterate.go └── maputil.go ├── README.md ├── stats ├── runtime.go └── README.md ├── sliceutils ├── sliceutil.go └── sliceutil_test.go ├── debugutils ├── requests.go ├── test │ ├── aggregator_test.go │ ├── pods_test.go │ └── resources_test.go ├── debugutils_suite_test.go ├── storage.go ├── mocks_kube_test.go └── aggregator_test.go ├── builtinutils └── builtinutil.go ├── envutils └── vars.go ├── template_util └── templateutil.go ├── pointersutils └── pointers.go └── parseutils └── parseutil.go /testutils/testutil_test.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | -------------------------------------------------------------------------------- /wmanager/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rfyiamcool/wmanager 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /grpcx/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/ms-go-commons/master/grpcx/logo.png -------------------------------------------------------------------------------- /shell/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/ms-go-commons/master/shell/logo.png -------------------------------------------------------------------------------- /healthutils/interface.go: -------------------------------------------------------------------------------- 1 | package healthchecker 2 | 3 | type HealthChecker interface { 4 | Fail() 5 | } 6 | -------------------------------------------------------------------------------- /testutils/helper/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM cjimti/go-echo:latest 2 | 3 | RUN apk update 4 | RUN apk add curl 5 | 6 | WORKDIR / 7 | 8 | ENTRYPOINT ["/tcp-echo"] -------------------------------------------------------------------------------- /dockerutils/save.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | // Save saves image to dest, as in `docker save` 4 | func Save(image, dest string) error { 5 | return Command("save", "-o", dest, image).Run() 6 | } 7 | -------------------------------------------------------------------------------- /testutils/runners/ports.go: -------------------------------------------------------------------------------- 1 | package runners 2 | 3 | import "github.com/onsi/ginkgo" 4 | 5 | func AllocateParallelPort(basePort int) int { 6 | return basePort + (ginkgo.GinkgoParallelNode()-1)*20 7 | } 8 | -------------------------------------------------------------------------------- /grpcx/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rfyiamcool/grpcpp 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 8 | google.golang.org/grpc v1.29.1 9 | ) 10 | -------------------------------------------------------------------------------- /logutils/logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | // Logger ... 4 | type Logger interface { 5 | Print(f Formatable) 6 | } 7 | 8 | // Formatable ... 9 | type Formatable interface { 10 | String() string 11 | JSON() string 12 | } 13 | -------------------------------------------------------------------------------- /versionutils/dep/types.go: -------------------------------------------------------------------------------- 1 | package dep 2 | 3 | type VersionType int 4 | 5 | const ( 6 | Revision VersionType = iota 7 | Version 8 | Branch 9 | ) 10 | 11 | type VersionInfo struct { 12 | Version string 13 | Type VersionType 14 | } 15 | -------------------------------------------------------------------------------- /configutils/test/config_suite_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestConfig(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Config Suite") 13 | } 14 | -------------------------------------------------------------------------------- /dockerutils/docker_suite_test.go: -------------------------------------------------------------------------------- 1 | package docker_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestDocker(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Docker Suite") 13 | } 14 | -------------------------------------------------------------------------------- /vfsutils/vfs_suite_test.go: -------------------------------------------------------------------------------- 1 | package vfsutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestVfsutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Vfsutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /testutils/helper/helper_suite_test.go: -------------------------------------------------------------------------------- 1 | package helper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestHelper(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Helper Suite") 13 | } 14 | -------------------------------------------------------------------------------- /certutils/certutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package certutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestCertutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Certutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /kubectrlutils/kubeutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package kubeutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestKubeutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Kubeutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /kubesetuputils/shared_suite_test.go: -------------------------------------------------------------------------------- 1 | package kubeinstallutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestShared(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Shared Suite") 13 | } 14 | -------------------------------------------------------------------------------- /nameutils/nameutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package nameutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestNameutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Nameutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /shell/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rfyiamcool/go-shell 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/stretchr/objx v0.2.0 // indirect 8 | github.com/stretchr/testify v1.6.1 9 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /slackutils/slack_suite_test.go: -------------------------------------------------------------------------------- 1 | package slackutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestSlackutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Slackutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /testutils/testutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package testutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestTestutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Testutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /versionutils/kubeapi/kubeapi_suite_test.go: -------------------------------------------------------------------------------- 1 | package kubeapi_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestKubeApi(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Kube Api Suite") 13 | } 14 | -------------------------------------------------------------------------------- /contextutils/time.go: -------------------------------------------------------------------------------- 1 | package contextutils 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | func Sleep(ctx context.Context, amount time.Duration) error { 9 | select { 10 | case <-time.After(amount): 11 | return nil 12 | case <-ctx.Done(): 13 | return ctx.Err() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /botutils/consts.go: -------------------------------------------------------------------------------- 1 | package botutils 2 | 3 | const ( 4 | PrType = "pull_request" 5 | PrReviewType = "pull_request_review" 6 | IssueCommentType = "issue_comment" 7 | CommitCommentType = "commit_comment" 8 | ReleaseType = "release" 9 | IssuesType = "issues" 10 | ) 11 | -------------------------------------------------------------------------------- /githubutils/githubutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package githubutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestGithubutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Githubutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /pkgmgmtutils/pkgmgmtutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package pkgmgmtutils 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestPkgmgmtutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Pkgmgmtutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /surveyutils/surveyutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package surveyutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestSurveyutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Surveyutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /healthutils/healthchecker_suite_test.go: -------------------------------------------------------------------------------- 1 | package healthchecker_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestHealthchecker(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Healthchecker Suite") 13 | } 14 | -------------------------------------------------------------------------------- /kubeinstallutils/helmchart/helmchart_suite_test.go: -------------------------------------------------------------------------------- 1 | package helmchart_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestHelmchart(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Helmchart Suite") 13 | } 14 | -------------------------------------------------------------------------------- /kubeinstallutils/installutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package installutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestInstallutils(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Installutils Suite") 13 | } 14 | -------------------------------------------------------------------------------- /kubeinstallutils/kubeinstall/kubeinstall_suite_test.go: -------------------------------------------------------------------------------- 1 | package kubeinstall_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestKubeinstall(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Kubeinstall Suite") 13 | } 14 | -------------------------------------------------------------------------------- /kubeinstallutils/kuberesource/kuberesource_suite_test.go: -------------------------------------------------------------------------------- 1 | package kuberesource_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestKuberesource(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Kuberesource Suite") 13 | } 14 | -------------------------------------------------------------------------------- /testutils/logs.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "github.com/fgrosse/zaptest" 5 | "github.com/wx-chevalier/go-utils/contextutils" 6 | 7 | . "github.com/onsi/ginkgo" 8 | ) 9 | 10 | func SetupLog() { 11 | logger := zaptest.LoggerWriter(GinkgoWriter) 12 | contextutils.SetFallbackLogger(logger.Sugar()) 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /protoutils/protoutil_suite_test.go: -------------------------------------------------------------------------------- 1 | package protoutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wx-chevalier/go-utils/log" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestProtoutil(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | log.DefaultOut = GinkgoWriter 15 | RunSpecs(t, "Protoutil Suite") 16 | } 17 | -------------------------------------------------------------------------------- /testutils/make.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "strings" 7 | 8 | . "github.com/onsi/gomega" 9 | ) 10 | 11 | func MustMake(dir, args string) { 12 | make := exec.Command("make", strings.Split(args, " ")...) 13 | make.Dir = dir 14 | out, err := make.CombinedOutput() 15 | if err != nil { 16 | fmt.Printf(string(out)) 17 | } 18 | Expect(err).NotTo(HaveOccurred()) 19 | } 20 | -------------------------------------------------------------------------------- /testutils/skip.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/wx-chevalier/go-utils/log" 7 | ) 8 | 9 | func AreTestsDisabled() bool { 10 | if os.Getenv("RUN_KUBE2E_TESTS") != "1" { 11 | log.Warnf("This test requires a running kubernetes cluster and is disabled by default. " + 12 | "To enable, set RUN_KUBE2E_TESTS=1 in your env.") 13 | return true 14 | } 15 | return false 16 | } 17 | -------------------------------------------------------------------------------- /testutils/goimpl/goimpl_suite_test.go: -------------------------------------------------------------------------------- 1 | package goimpl 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wx-chevalier/go-utils/testutils" 7 | 8 | . "github.com/onsi/ginkgo" 9 | ) 10 | 11 | func TestGoImpl(t *testing.T) { 12 | 13 | testutils.RegisterPreFailHandler( 14 | func() { 15 | testutils.PrintTrimmedStack() 16 | }) 17 | testutils.RegisterCommonFailHandlers() 18 | RunSpecs(t, "Go Impl Suite") 19 | } 20 | -------------------------------------------------------------------------------- /funcutils/rand.go: -------------------------------------------------------------------------------- 1 | package randutils 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | 12 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") 13 | 14 | func RandString(length int) string { 15 | b := make([]rune, length) 16 | for i := range b { 17 | b[i] = letterRunes[rand.Intn(len(letterRunes))] 18 | } 19 | return string(b) 20 | } 21 | -------------------------------------------------------------------------------- /testutils/random.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().UnixNano()) 10 | } 11 | 12 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") 13 | 14 | func RandString(length int) string { 15 | b := make([]rune, length) 16 | for i := range b { 17 | b[i] = letterRunes[rand.Intn(len(letterRunes))] 18 | } 19 | return string(b) 20 | } 21 | -------------------------------------------------------------------------------- /versionutils/kubeapi/list.go: -------------------------------------------------------------------------------- 1 | package kubeapi 2 | 3 | // VersionList implements sort.Interface for a list of Kubernetes API version tags. 4 | type VersionList []Version 5 | 6 | func (list VersionList) Len() int { 7 | return len(list) 8 | } 9 | 10 | func (list VersionList) Less(i, j int) bool { 11 | return list[i].LessThan(list[j]) 12 | } 13 | 14 | func (list VersionList) Swap(i, j int) { 15 | list[i], list[j] = list[j], list[i] 16 | } 17 | -------------------------------------------------------------------------------- /errutils/aggregate_errs.go: -------------------------------------------------------------------------------- 1 | package errutils 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func AggregateErrs(ctx context.Context, dest chan error, src <-chan error, srcInfo string) { 8 | for { 9 | select { 10 | case err, ok := <-src: 11 | if !ok { 12 | return 13 | } 14 | select { 15 | case <-ctx.Done(): 16 | return 17 | case dest <- Wrapf(err, srcInfo): 18 | } 19 | case <-ctx.Done(): 20 | return 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fileutils/file_crud_test.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCopyFileErrorIfDirectory(t *testing.T) { 10 | t.Log("It fails if source is a directory") 11 | { 12 | tmpFolder, err := NormalizedOSTempDirPath("_tmp") 13 | require.NoError(t, err) 14 | require.EqualError(t, CopyFile(tmpFolder, "./nothing/whatever"), "Source is a directory: "+tmpFolder) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /logutils/raw_logger_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestRawPrint(t *testing.T) { 11 | t.Log("Custom Formattable") 12 | { 13 | var b bytes.Buffer 14 | logger := NewRawLogger(&b) 15 | 16 | test := TestFormattable{ 17 | A: "log", 18 | B: "test", 19 | } 20 | 21 | logger.Print(test) 22 | require.Equal(t, "log test\n", b.String()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /strutils/regex.go: -------------------------------------------------------------------------------- 1 | package regexputil 2 | 3 | import "regexp" 4 | 5 | // NamedFindStringSubmatch ... 6 | func NamedFindStringSubmatch(rexp *regexp.Regexp, text string) (map[string]string, bool) { 7 | match := rexp.FindStringSubmatch(text) 8 | if match == nil { 9 | return nil, false 10 | } 11 | result := map[string]string{} 12 | for i, name := range rexp.SubexpNames() { 13 | if i != 0 { 14 | result[name] = match[i] 15 | } 16 | } 17 | return result, true 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}", 13 | "env": {}, 14 | "args": [] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /testutils/get_current_file_test.go: -------------------------------------------------------------------------------- 1 | package testutils_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | . "github.com/wx-chevalier/go-utils/testutils" 8 | ) 9 | 10 | var _ = Describe("GetCurrentFileDirectory", func() { 11 | It("works", func() { 12 | f, err := GetCurrentFile() 13 | Expect(err).NotTo(HaveOccurred()) 14 | Expect(f).To(HaveSuffix("testutils/get_current_file_test.go")) 15 | Expect(f).To(HavePrefix("/")) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /testutils/get_current_file.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | 7 | "github.com/wx-chevalier/go-utils/errutils" 8 | ) 9 | 10 | // returns the absolute path to the file the caller 11 | // intended to provide a way to find test files 12 | func GetCurrentFile() (string, error) { 13 | _, callerFile, _, ok := runtime.Caller(1) 14 | if !ok { 15 | return "", errors.Errorf("failed to get runtime.Caller") 16 | } 17 | return filepath.Abs(callerFile) 18 | } 19 | -------------------------------------------------------------------------------- /versionutils/versionutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package versionutils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wx-chevalier/go-utils/testutils" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestVersionUtils(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | testutils.RegisterPreFailHandler( 15 | func() { 16 | testutils.PrintTrimmedStack() 17 | }) 18 | testutils.RegisterCommonFailHandlers() 19 | RunSpecs(t, "Versionutils Suite") 20 | } 21 | -------------------------------------------------------------------------------- /versionutils/cli.go: -------------------------------------------------------------------------------- 1 | package versionutils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func GetReleaseVersionOrExitGracefully() *Version { 9 | tag, present := os.LookupEnv("TAGGED_VERSION") 10 | if !present || tag == "" { 11 | fmt.Printf("TAGGED_VERSION not found in environment.\n") 12 | os.Exit(0) 13 | } 14 | version, err := ParseVersion(tag) 15 | if err != nil { 16 | fmt.Printf("TAGGED_VERSION %s is not a valid semver version.\n", tag) 17 | os.Exit(0) 18 | } 19 | return version 20 | } 21 | -------------------------------------------------------------------------------- /osutils/client.go: -------------------------------------------------------------------------------- 1 | package osutils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | ) 7 | 8 | type OsClient interface { 9 | Getenv(key string) string 10 | ReadFile(path string) ([]byte, error) 11 | } 12 | 13 | type osClient struct { 14 | } 15 | 16 | func (*osClient) Getenv(key string) string { 17 | return os.Getenv(key) 18 | } 19 | 20 | func (*osClient) ReadFile(path string) ([]byte, error) { 21 | return ioutil.ReadFile(path) 22 | } 23 | 24 | func NewOsClient() OsClient { 25 | return &osClient{} 26 | } 27 | -------------------------------------------------------------------------------- /manifestutils/test/example_suite_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/wx-chevalier/go-utils/manifesttestutils" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestManifestTestUtils(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "ManifestTestUtils Suite") 15 | } 16 | 17 | var ( 18 | testManifest TestManifest 19 | ) 20 | 21 | var _ = BeforeSuite(func() { 22 | testManifest = NewTestManifest("example.yaml") 23 | }) 24 | -------------------------------------------------------------------------------- /testutils/port/port.go: -------------------------------------------------------------------------------- 1 | package port 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "github.com/onsi/ginkgo/config" 7 | ) 8 | 9 | var MaxTests = 1000 10 | 11 | type TestPort struct { 12 | port *uint32 13 | } 14 | 15 | // Helps you get a free port with ginkgo tests. 16 | func NewTestPort(initial uint32) TestPort { 17 | return TestPort{ 18 | port: &initial, 19 | } 20 | } 21 | 22 | func (t TestPort) NextPort() uint32 { 23 | return atomic.AddUint32(t.port, 1) + uint32(config.GinkgoConfig.ParallelNode*MaxTests) 24 | } 25 | -------------------------------------------------------------------------------- /kubeinstallutils/manifests_test.go: -------------------------------------------------------------------------------- 1 | package installutils_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | "github.com/wx-chevalier/go-utils/installutils" 7 | ) 8 | 9 | var _ = Describe("Manifests", func() { 10 | It("works", func() { 11 | manifests, err := installutils.GetManifestsFromRemoteTar("https://github.com/XiaoMi/naftis/releases/download/0.1.4-rc6/manifest.tar.gz") 12 | Expect(err).NotTo(HaveOccurred()) 13 | Expect(len(manifests)).To(BeEquivalentTo(3)) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /testutils/goimpl/curl_test.go: -------------------------------------------------------------------------------- 1 | package goimpl 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("Curl", func() { 9 | 10 | It("should query a url", func() { 11 | soloResp, err := Curl("https://www.solo.io") 12 | Expect(err).NotTo(HaveOccurred()) 13 | Expect(len(soloResp)).To(BeNumerically(">", 0)) 14 | }) 15 | 16 | It("should error on invalid url", func() { 17 | _, err := Curl("invalid://www.solo.io") 18 | Expect(err).To(HaveOccurred()) 19 | }) 20 | 21 | }) 22 | -------------------------------------------------------------------------------- /botutils/botconfig/botconfig_suite_test.go: -------------------------------------------------------------------------------- 1 | package botconfig_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wx-chevalier/go-utils/testutils" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestBotconfig(t *testing.T) { 13 | test = t 14 | RegisterFailHandler(Fail) 15 | testutils.RegisterPreFailHandler( 16 | func() { 17 | testutils.PrintTrimmedStack() 18 | }) 19 | testutils.RegisterCommonFailHandlers() 20 | RunSpecs(t, "Botconfig Suite") 21 | } 22 | 23 | var test *testing.T 24 | -------------------------------------------------------------------------------- /kubeinstallutils/test/inputs/input_manifest.go: -------------------------------------------------------------------------------- 1 | package inputs 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wx-chevalier/go-utils/installutils/helmchart" 7 | 8 | "github.com/onsi/gomega" 9 | ) 10 | 11 | func InputGlooManifests(ns string) helmchart.Manifests { 12 | manifests, err := helmchart.RenderManifests( 13 | context.TODO(), 14 | "https://storage.googleapis.com/solo-public-helm/charts/gloo-1.0.0.tgz", 15 | "", 16 | "yella", 17 | ns, 18 | "", 19 | ) 20 | gomega.ExpectWithOffset(1, err).NotTo(gomega.HaveOccurred()) 21 | return manifests 22 | } 23 | -------------------------------------------------------------------------------- /progressutils/wrapper_test.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package progress 4 | 5 | import ( 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestNewWrapper(t *testing.T) { 11 | message := "loading" 12 | spinner := NewDefaultSpinner(message) 13 | 14 | isInteractiveMode := true 15 | NewWrapper(spinner, isInteractiveMode).WrapAction(func() { 16 | time.Sleep(2 * time.Second) 17 | }) 18 | } 19 | 20 | func TestNewDefaultWrapper(t *testing.T) { 21 | message := "loading" 22 | NewDefaultWrapper(message).WrapAction(func() { 23 | time.Sleep(2 * time.Second) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /nameutils/sanitize_name.go: -------------------------------------------------------------------------------- 1 | package nameutils 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var badChars = []rune{ 10 | '.', 11 | '_', 12 | } 13 | 14 | // sanitize name to make it clean for writing kubernetes objects 15 | func SanitizeName(name string) string { 16 | name = strings.Map(func(r rune) rune { 17 | for _, badChar := range badChars { 18 | if r == badChar { 19 | return '-' 20 | } 21 | } 22 | return r 23 | }, name) 24 | if len(name) > 63 { 25 | hash := md5.Sum([]byte(name)) 26 | name = fmt.Sprintf("%s-%x", name[:46], hash[:8]) 27 | } 28 | return name 29 | } 30 | -------------------------------------------------------------------------------- /grpcx/README.md: -------------------------------------------------------------------------------- 1 | xiaorui.cc grpcpp 2 | 3 | ## grpcx 4 | 5 | `grpcx` golang grpc common 6 | 7 | ## Desc 8 | 9 | **middleware** 10 | 11 | * unary 12 | * stream 13 | 14 | **interceptor** 15 | 16 | * logger 17 | * try catch 18 | * rate limiter 19 | * set custom logger 20 | 21 | **get ip info** 22 | 23 | * get real ip 24 | * get peer ip 25 | 26 | **creds** 27 | 28 | * easy new server creds 29 | * easy new client creds 30 | 31 | **conn state** 32 | 33 | * check grpc connection state 34 | 35 | **status code** 36 | 37 | * easy new status 38 | * check status 39 | * diff error 40 | -------------------------------------------------------------------------------- /commandutils/git/git.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/wx-chevalier/go-utils/commandutils" 7 | ) 8 | 9 | // Git represents a Git project. 10 | type Git struct { 11 | dir string 12 | } 13 | 14 | // New creates a new git project. 15 | func New(dir string) (Git, error) { 16 | if err := os.MkdirAll(dir, 0755); err != nil { 17 | return Git{}, err 18 | } 19 | return Git{dir: dir}, nil 20 | } 21 | 22 | func (g *Git) command(args ...string) *command.Model { 23 | cmd := command.New("git", args...) 24 | cmd.SetDir(g.dir) 25 | cmd.SetEnvs(append(os.Environ(), "GIT_ASKPASS=echo")...) 26 | return cmd 27 | } 28 | -------------------------------------------------------------------------------- /progressutils/spinner_test.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package progress 4 | 5 | import ( 6 | "os" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestNewSpinner(t *testing.T) { 12 | message := "loading" 13 | chars := []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"} 14 | delay := 100 * time.Millisecond 15 | writer := os.Stdout 16 | 17 | spinner := NewSpinner(message, chars, delay, writer) 18 | spinner.Start() 19 | time.Sleep(2 * time.Second) 20 | spinner.Stop() 21 | } 22 | 23 | func TestNewDefaultSpinner(t *testing.T) { 24 | message := "loading" 25 | spinner := NewDefaultSpinner(message) 26 | spinner.Start() 27 | time.Sleep(2 * time.Second) 28 | spinner.Stop() 29 | } 30 | -------------------------------------------------------------------------------- /logutils/json_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | // JSONLoger ... 10 | type JSONLoger struct { 11 | writer io.Writer 12 | } 13 | 14 | // NewJSONLoger ... 15 | func NewJSONLoger(writer io.Writer) *JSONLoger { 16 | return &JSONLoger{ 17 | writer: writer, 18 | } 19 | } 20 | 21 | // NewDefaultJSONLoger ... 22 | func NewDefaultJSONLoger() JSONLoger { 23 | return JSONLoger{ 24 | writer: os.Stdout, 25 | } 26 | } 27 | 28 | // Print ... 29 | func (l JSONLoger) Print(f Formatable) { 30 | if _, err := fmt.Fprint(l.writer, f.JSON()); err != nil { 31 | fmt.Printf("failed to print message: %s, error: %s\n", f.JSON(), err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /logutils/raw_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | // RawLogger ... 10 | type RawLogger struct { 11 | writer io.Writer 12 | } 13 | 14 | // NewRawLogger ... 15 | func NewRawLogger(writer io.Writer) *RawLogger { 16 | return &RawLogger{ 17 | writer: writer, 18 | } 19 | } 20 | 21 | // NewDefaultRawLogger ... 22 | func NewDefaultRawLogger() RawLogger { 23 | return RawLogger{ 24 | writer: os.Stdout, 25 | } 26 | } 27 | 28 | // Print ... 29 | func (l RawLogger) Print(f Formatable) { 30 | if _, err := fmt.Fprintln(l.writer, f.String()); err != nil { 31 | fmt.Printf("failed to print message: %s, error: %s\n", f.String(), err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /logutils/log_s.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "time" 8 | ) 9 | 10 | var outWriter io.Writer = os.Stdout 11 | 12 | // SetOutWriter ... 13 | func SetOutWriter(writer io.Writer) { 14 | outWriter = writer 15 | } 16 | 17 | var enableDebugLog = false 18 | 19 | // SetEnableDebugLog ... 20 | func SetEnableDebugLog(enable bool) { 21 | enableDebugLog = enable 22 | } 23 | 24 | var timestampLayout = "15:04:05" 25 | 26 | // SetTimestampLayout ... 27 | func SetTimestampLayout(layout string) { 28 | timestampLayout = layout 29 | } 30 | 31 | func timestampField() string { 32 | currentTime := time.Now() 33 | return fmt.Sprintf("[%s]", currentTime.Format(timestampLayout)) 34 | } 35 | -------------------------------------------------------------------------------- /pkcs12utils/internal/rc2/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package rc2 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func BenchmarkEncrypt(b *testing.B) { 12 | r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) 13 | b.ResetTimer() 14 | var src [8]byte 15 | for i := 0; i < b.N; i++ { 16 | r.Encrypt(src[:], src[:]) 17 | } 18 | } 19 | 20 | func BenchmarkDecrypt(b *testing.B) { 21 | r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) 22 | b.ResetTimer() 23 | var src [8]byte 24 | for i := 0; i < b.N; i++ { 25 | r.Decrypt(src[:], src[:]) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /nameutils/sanitize_name_test.go: -------------------------------------------------------------------------------- 1 | package nameutils_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | "k8s.io/apimachinery/pkg/util/validation" 7 | 8 | . "github.com/wx-chevalier/go-utils/nameutils" 9 | ) 10 | 11 | var _ = Describe("SanitizeName", func() { 12 | expected := "istio-release-testu4j66c6y-details-istio-routi-59cd38e6f450a2fd" 13 | It("makes names dns-compliant", func() { 14 | name := "istio-release-testu4j66c6y-details-istio-routing-testfi4kb3wb-svc-cluster-local" 15 | actual := SanitizeName(name) 16 | Expect(len(actual)).To(Equal(63)) 17 | Expect(actual).To(Equal(expected)) 18 | Expect(validation.IsDNS1123Label(actual)).To(HaveLen(0)) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /vfsutils/vfs_test.go: -------------------------------------------------------------------------------- 1 | package vfsutils_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | "github.com/wx-chevalier/go-utils/vfsutils" 7 | "github.com/spf13/afero" 8 | ) 9 | 10 | var _ = Describe("vfsutils", func() { 11 | Context("MountTar", func() { 12 | It("works", func() { 13 | fs := afero.NewMemMapFs() 14 | dir, err := vfsutils.MountTar(fs, "https://github.com/XiaoMi/naftis/releases/download/0.1.4-rc6/manifest.tar.gz") 15 | Expect(err).NotTo(HaveOccurred()) 16 | Expect(dir).NotTo(BeEquivalentTo("")) 17 | files, err := afero.ReadDir(fs, dir) 18 | Expect(err).NotTo(HaveOccurred()) 19 | Expect(len(files)).To(BeEquivalentTo(3)) 20 | }) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /testutils/goimpl/curl.go: -------------------------------------------------------------------------------- 1 | package goimpl 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | // Curl provides some of the functionality you would expect from the curl command. 10 | // This saves you from having to call the curl binary in environments where that binary is not available. 11 | func Curl(url string) (string, error) { 12 | body := bytes.NewReader([]byte(url)) 13 | req, err := http.NewRequest("GET", url, body) 14 | if err != nil { 15 | return "", err 16 | } 17 | 18 | resp, err := http.DefaultClient.Do(req) 19 | if err != nil { 20 | return "", err 21 | } 22 | p := new(bytes.Buffer) 23 | _, err = io.Copy(p, resp.Body) 24 | defer resp.Body.Close() 25 | 26 | return p.String(), nil 27 | } 28 | -------------------------------------------------------------------------------- /kubesetuputils/kube_errs.go: -------------------------------------------------------------------------------- 1 | package kubeerrutils 2 | 3 | import ( 4 | "strings" 5 | 6 | kubeerrs "k8s.io/apimachinery/pkg/api/errors" 7 | "k8s.io/apimachinery/pkg/api/validation" 8 | ) 9 | 10 | const ObjectIsBeingDeletedErrorMsg = "object is being deleted" 11 | 12 | func IsImmutableErr(err error) bool { 13 | if err != nil { 14 | return kubeerrs.IsInvalid(err) && strings.Contains(err.Error(), validation.FieldImmutableErrorMsg) 15 | } 16 | return false 17 | } 18 | 19 | // is the error AlreadyExists && the resource is not terminating? 20 | func IsAlreadyExists(err error) bool { 21 | if err != nil { 22 | return kubeerrs.IsAlreadyExists(err) && !strings.Contains(err.Error(), ObjectIsBeingDeletedErrorMsg) 23 | } 24 | return false 25 | 26 | } 27 | -------------------------------------------------------------------------------- /maputils/ordered_map_iterate.go: -------------------------------------------------------------------------------- 1 | package maputils 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // thank you R.P. 8 | // TODO(ilackarms): find a better way to solve generics than interface{} 9 | // consider using go-utils to generate 10 | func OrderedMapIterator(m map[string]interface{}, onKey func(key string, value interface{})) { 11 | var list []struct { 12 | key string 13 | value interface{} 14 | } 15 | for k, v := range m { 16 | list = append(list, struct { 17 | key string 18 | value interface{} 19 | }{ 20 | key: k, 21 | value: v, 22 | }) 23 | } 24 | sort.SliceStable(list, func(i, j int) bool { 25 | return list[i].key < list[j].key 26 | }) 27 | for _, el := range list { 28 | onKey(el.key, el.value) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /strutils/hash.go: -------------------------------------------------------------------------------- 1 | package hashutils 2 | 3 | import ( 4 | "github.com/mitchellh/hashstructure" 5 | ) 6 | 7 | // Hashers are resources which have a custom hashing function defined. 8 | // Hash functions are generated by default for go-utils resources 9 | type Hasher interface { 10 | Hash() uint64 11 | } 12 | 13 | // hash one or more values 14 | // order matters 15 | func HashAll(values ...interface{}) uint64 { 16 | var hashes []uint64 17 | for _, v := range values { 18 | hashes = append(hashes, hashValue(v)) 19 | } 20 | return hashValue(hashes) 21 | } 22 | 23 | func hashValue(val interface{}) uint64 { 24 | if hasher, ok := val.(Hasher); ok { 25 | return hasher.Hash() 26 | } 27 | h, err := hashstructure.Hash(val, nil) 28 | if err != nil { 29 | panic("resource failed to hash: " + err.Error()) 30 | } 31 | return h 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

Go Utils

4 |

5 | 6 | This repo contains common utilities for go projects. 7 | 8 | > 更多 Go 学习笔记参考 [:books: Go-Series, Go From Zero to Hero. | 语法基础、工程实践、并发编程、Web 开发](https://github.com/wx-chevalier/Go-Series)。 9 | 10 | # About 11 | 12 | - [go-realworld-clean #Project#](https://github.com/err0r500/go-realworld-clean): Golang clean-architecture codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API. 13 | 14 | - [golang-gin-realworld-example-app](https://github.com/gothinkster/golang-gin-realworld-example-app) 15 | 16 | - [d3ta-go](https://github.com/muharihar/d3ta-go): A Simple Implementation of Domain-Driven Design Technical Architecture Patterns in Go. 17 | -------------------------------------------------------------------------------- /logutils/dummylogger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | // DummyLogger ... 4 | type DummyLogger struct{} 5 | 6 | // NewDummyLogger ... 7 | func NewDummyLogger() DummyLogger { 8 | return DummyLogger{} 9 | } 10 | 11 | // Donef ... 12 | func (dl DummyLogger) Donef(format string, v ...interface{}) {} 13 | 14 | // Successf ... 15 | func (dl DummyLogger) Successf(format string, v ...interface{}) {} 16 | 17 | // Infof ... 18 | func (dl DummyLogger) Infof(format string, v ...interface{}) {} 19 | 20 | // Printf ... 21 | func (dl DummyLogger) Printf(format string, v ...interface{}) {} 22 | 23 | // Debugf ... 24 | func (dl DummyLogger) Debugf(format string, v ...interface{}) {} 25 | 26 | // Warnf ... 27 | func (dl DummyLogger) Warnf(format string, v ...interface{}) {} 28 | 29 | // Errorf ... 30 | func (dl DummyLogger) Errorf(format string, v ...interface{}) {} 31 | -------------------------------------------------------------------------------- /testutils/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Fail Handlers 3 | 4 | ## PrintTrimmedStack 5 | 6 | The `PrintTrimmedStack` fail handler simplifies error tracking in ginkgo tests by printing a condensed stack trace upon failure. Printout excludes well-known overhead files so you can more easily sight the failing line. This eliminates the need to count stack offset via `ExpectWithOffset`. You can just use `Expect`. 7 | 8 | 9 | ### Usage 10 | 11 | To use, register `PrintTrimmedStack` as a prefail handler with `RegisterPreFailHandler` in your `ginkgo` suite: 12 | 13 | ```go 14 | func TestCliCore(t *testing.T) { 15 | 16 | testutils.RegisterPreFailHandler( 17 | func() { 18 | testutils.PrintTrimmedStack() 19 | }) 20 | testutils.RegisterCommonFailHandlers() 21 | RegisterFailHandler(Fail) 22 | testutils.SetupLog() 23 | RunSpecs(t, "Clicore Suite") 24 | } 25 | ``` -------------------------------------------------------------------------------- /configutils/config_map_client_mock.go: -------------------------------------------------------------------------------- 1 | package configutils 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | type MockConfigMapClient struct { 11 | Data map[string]string 12 | GetError error 13 | SetError error 14 | } 15 | 16 | func (c *MockConfigMapClient) GetConfigMap(ctx context.Context, namespace string, name string) (*v1.ConfigMap, error) { 17 | if c.GetError != nil { 18 | return nil, c.GetError 19 | } 20 | return &v1.ConfigMap{ 21 | ObjectMeta: metav1.ObjectMeta{ 22 | Namespace: namespace, 23 | Name: name, 24 | }, 25 | Data: c.Data, 26 | }, nil 27 | } 28 | 29 | func (c *MockConfigMapClient) SetConfigMap(ctx context.Context, configMap *v1.ConfigMap) error { 30 | if c.SetError != nil { 31 | return c.SetError 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkcs12utils/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkcs12 6 | 7 | import "errors" 8 | 9 | var ( 10 | // ErrDecryption represents a failure to decrypt the input. 11 | ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") 12 | 13 | // ErrIncorrectPassword is returned when an incorrect password is detected. 14 | // Usually, P12/PFX data is signed to be able to verify the password. 15 | ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") 16 | ) 17 | 18 | // NotImplementedError indicates that the input is not currently supported. 19 | type NotImplementedError string 20 | 21 | func (e NotImplementedError) Error() string { 22 | return "pkcs12: " + string(e) 23 | } 24 | -------------------------------------------------------------------------------- /logutils/json_logger_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | type TestFormattable struct { 12 | A string `json:"a,omitempty"` 13 | B string `json:"b,omitempty"` 14 | } 15 | 16 | // String ... 17 | func (f TestFormattable) String() string { 18 | return fmt.Sprintf("%s %s", f.A, f.B) 19 | } 20 | 21 | // JSON ... 22 | func (f TestFormattable) JSON() string { 23 | return fmt.Sprintf(`{"a":"%s","b":"%s"}`, f.A, f.B) 24 | } 25 | 26 | func TestJSONPrint(t *testing.T) { 27 | 28 | t.Log("Custom Formattable") 29 | { 30 | var b bytes.Buffer 31 | logger := NewJSONLoger(&b) 32 | 33 | test := TestFormattable{ 34 | A: "log", 35 | B: "test", 36 | } 37 | 38 | logger.Print(test) 39 | require.Equal(t, `{"a":"log","b":"test"}`, b.String()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fileutils/messages.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/ghodss/yaml" 7 | "github.com/gogo/protobuf/proto" 8 | "github.com/pkg/errors" 9 | protoutils "github.com/wx-chevalier/go-utils/protoutils" 10 | ) 11 | 12 | func WriteToFile(filename string, pb proto.Message) error { 13 | jsn, err := protoutils.MarshalBytes(pb) 14 | if err != nil { 15 | return err 16 | } 17 | data, err := yaml.JSONToYAML(jsn) 18 | if err != nil { 19 | return err 20 | } 21 | return ioutil.WriteFile(filename, data, 0644) 22 | } 23 | 24 | func ReadFileInto(filename string, v proto.Message) error { 25 | data, err := ioutil.ReadFile(filename) 26 | if err != nil { 27 | return errors.Errorf("error reading file: %v", err) 28 | } 29 | jsn, err := yaml.YAMLToJSON(data) 30 | if err != nil { 31 | return err 32 | } 33 | return protoutils.UnmarshalBytes(jsn, v) 34 | } 35 | -------------------------------------------------------------------------------- /botutils/README.md: -------------------------------------------------------------------------------- 1 | # Botutils 2 | 3 | This package contains utilities for writing simple git bot applications. 4 | 5 | ## Implementing hooks 6 | 7 | Write a plugin that implements one or more of the handler interfaces defined in `interface.go`. 8 | 9 | ## Implementing a server 10 | 11 | Create an instance of SimpleGitBot: 12 | 13 | ```go 14 | func Run(ctx context.Context) error { 15 | staticCfg := botutils.StaticBotConfig{BotName: BotName, Version: version.Version} 16 | bot := botutils.NewSimpleGitBot(staticCfg) 17 | return bot.Start(context.TODO(), plugins...) 18 | } 19 | ``` 20 | 21 | ## Deploying the bot 22 | 23 | The bot needs to be deployed with a config that can be deserialized into the `botconfig.Config` struct. By default, 24 | this should be available at `/etc/solo-github-app/config.yml`, but can be mounted to a custom location 25 | by setting the `BOT_CONFIG` environment variable. -------------------------------------------------------------------------------- /testutils/deploy_from_yaml.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "github.com/wx-chevalier/go-utils/kubeinstallutils" 5 | "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 6 | "k8s.io/client-go/kubernetes" 7 | "k8s.io/client-go/rest" 8 | ) 9 | 10 | func DeployFromYaml(cfg *rest.Config, namespace, yamlManifest string) error { 11 | kube, err := kubernetes.NewForConfig(cfg) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | apiext, err := clientset.NewForConfig(cfg) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | installer := kubeinstallutils.NewKubeInstaller(kube, apiext, namespace) 22 | 23 | kubeObjs, err := kubeinstallutils.ParseKubeManifest(yamlManifest) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | for _, kubeOjb := range kubeObjs { 29 | if err := installer.Create(kubeOjb); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /stats/runtime.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import ( 4 | "context" 5 | "runtime" 6 | "time" 7 | 8 | "go.opencensus.io/stats" 9 | "go.opencensus.io/stats/view" 10 | ) 11 | 12 | var ( 13 | MNumGoRoutines = stats.Int64("runtime/goroutines", "The number of goroutines", "1") 14 | 15 | GoroutinesNumView = &view.View{ 16 | Name: "runtime/goroutines", 17 | Measure: MNumGoRoutines, 18 | Description: "The number of goroutines", 19 | Aggregation: view.Sum(), 20 | } 21 | ) 22 | 23 | func init() { 24 | view.Register(GoroutinesNumView) 25 | } 26 | 27 | func RunGoroutineStat() { 28 | numgoroutines := int64(0) 29 | for { 30 | time.Sleep(time.Second) 31 | newnumgoroutines := int64(runtime.NumGoroutine()) 32 | diff := newnumgoroutines - numgoroutines 33 | numgoroutines = newnumgoroutines 34 | if diff != 0 { 35 | stats.Record(context.TODO(), MNumGoRoutines.M(diff)) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sliceutils/sliceutil.go: -------------------------------------------------------------------------------- 1 | package sliceutil 2 | 3 | // UniqueStringSlice - returns a cleaned up list, 4 | // where every item is unique. 5 | // Does NOT guarantee any ordering, the result can 6 | // be in any order! 7 | func UniqueStringSlice(strs []string) []string { 8 | lookupMap := map[string]interface{}{} 9 | for _, aStr := range strs { 10 | lookupMap[aStr] = 1 11 | } 12 | uniqueStrs := []string{} 13 | for k := range lookupMap { 14 | uniqueStrs = append(uniqueStrs, k) 15 | } 16 | return uniqueStrs 17 | } 18 | 19 | // IndexOfStringInSlice ... 20 | func IndexOfStringInSlice(searchFor string, searchIn []string) int { 21 | for idx, anItm := range searchIn { 22 | if anItm == searchFor { 23 | return idx 24 | } 25 | } 26 | return -1 27 | } 28 | 29 | // IsStringInSlice ... 30 | func IsStringInSlice(searchFor string, searchIn []string) bool { 31 | return IndexOfStringInSlice(searchFor, searchIn) >= 0 32 | } 33 | -------------------------------------------------------------------------------- /progressutils/progress.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // SimpleProgressE ... 9 | func SimpleProgressE(printChar string, tickInterval time.Duration, action func() error) error { 10 | var actionError error 11 | SimpleProgress(printChar, tickInterval, func() { 12 | actionError = action() 13 | }) 14 | return actionError 15 | } 16 | 17 | // SimpleProgress ... 18 | // action : have to be a synchronous action! 19 | // tickInterval : e.g. : 5000 * time.Millisecond 20 | func SimpleProgress(printChar string, tickInterval time.Duration, action func()) { 21 | // run async 22 | finishedChan := make(chan bool) 23 | 24 | go func() { 25 | action() 26 | finishedChan <- true 27 | }() 28 | 29 | isRunFinished := false 30 | for !isRunFinished { 31 | select { 32 | case <-finishedChan: 33 | isRunFinished = true 34 | case <-time.Tick(tickInterval): 35 | fmt.Print(printChar) 36 | } 37 | } 38 | fmt.Println() 39 | } 40 | -------------------------------------------------------------------------------- /debugutils/requests.go: -------------------------------------------------------------------------------- 1 | package debugutils 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "path/filepath" 7 | 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/client-go/rest" 10 | ) 11 | 12 | type LogMeta struct { 13 | PodMeta metav1.ObjectMeta 14 | ContainerName string 15 | } 16 | 17 | type LogsRequest struct { 18 | LogMeta 19 | Request rest.ResponseWrapper 20 | } 21 | 22 | type LogsResponse struct { 23 | LogMeta 24 | Response io.ReadCloser 25 | } 26 | 27 | func (lr LogMeta) ResourcePath(dir string) string { 28 | return filepath.Join(dir, lr.ResourceId()) 29 | } 30 | 31 | func (lr LogMeta) ResourceId() string { 32 | return fmt.Sprintf("%s_%s_%s.log", lr.PodMeta.Namespace, lr.PodMeta.Name, lr.ContainerName) 33 | } 34 | 35 | func NewLogsRequest(podMeta metav1.ObjectMeta, containerName string, request *rest.Request) *LogsRequest { 36 | return &LogsRequest{LogMeta: LogMeta{PodMeta: podMeta, ContainerName: containerName}, Request: request} 37 | } 38 | -------------------------------------------------------------------------------- /certutils/gen_cert_test.go: -------------------------------------------------------------------------------- 1 | package certutils_test 2 | 3 | import ( 4 | "crypto/x509" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | . "github.com/wx-chevalier/go-utils/certutils" 10 | "k8s.io/client-go/util/cert" 11 | ) 12 | 13 | var _ = Describe("GenCert", func() { 14 | It("successfully generates a cert for the given config", func() { 15 | certs, err := GenerateSelfSignedCertificate(cert.Config{ 16 | CommonName: "secure.af", 17 | Organization: []string{"solo.io"}, 18 | AltNames: cert.AltNames{ 19 | DNSNames: []string{ 20 | "secure.af", 21 | "secure.af.svc", 22 | "secure.af.svc.cluster.local", 23 | }, 24 | }, 25 | Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 26 | }) 27 | Expect(err).NotTo(HaveOccurred()) 28 | Expect(len(certs.CaCertificate)).To(BeNumerically(">", 1)) 29 | Expect(len(certs.ServerCertificate)).To(BeNumerically(">", 1)) 30 | Expect(len(certs.ServerCertKey)).To(BeNumerically(">", 1)) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /builtinutils/builtinutil.go: -------------------------------------------------------------------------------- 1 | package builtinutil 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // CastInterfaceToInterfaceSlice ... 9 | func CastInterfaceToInterfaceSlice(slice interface{}) ([]interface{}, error) { 10 | s := reflect.ValueOf(slice) 11 | if s.Kind() != reflect.Slice { 12 | return []interface{}{}, fmt.Errorf("Input is not a slice: %#v", slice) 13 | } 14 | 15 | ret := make([]interface{}, s.Len()) 16 | 17 | for i := 0; i < s.Len(); i++ { 18 | ret[i] = s.Index(i).Interface() 19 | } 20 | 21 | return ret, nil 22 | } 23 | 24 | // DeepEqualSlices ... 25 | func DeepEqualSlices(expected, actual []interface{}) bool { 26 | expectedMap := map[string]bool{} 27 | for _, itm := range expected { 28 | itmAsStr := fmt.Sprintf("%#v", itm) 29 | expectedMap[itmAsStr] = true 30 | } 31 | actualMap := map[string]bool{} 32 | for _, itm := range actual { 33 | itmAsStr := fmt.Sprintf("%#v", itm) 34 | actualMap[itmAsStr] = true 35 | } 36 | return reflect.DeepEqual(expectedMap, actualMap) 37 | } 38 | -------------------------------------------------------------------------------- /slackutils/http.go: -------------------------------------------------------------------------------- 1 | package slackutils 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "net/http" 8 | 9 | "github.com/wx-chevalier/go-utils/contextutils" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | type HttpClient interface { 14 | PostJsonContent(ctx context.Context, message, url string) 15 | } 16 | 17 | type DefaultHttpClient struct{} 18 | 19 | func (*DefaultHttpClient) PostJsonContent(ctx context.Context, message, url string) { 20 | type Payload struct { 21 | Text string `json:"text"` 22 | } 23 | 24 | data := Payload{ 25 | Text: message, 26 | } 27 | payloadBytes, err := json.Marshal(data) 28 | if err != nil { 29 | contextutils.LoggerFrom(ctx).Errorw("Notifying slack failed", zap.Error(err)) 30 | return 31 | } 32 | body := bytes.NewReader(payloadBytes) 33 | 34 | req, err := http.Post(url, "application/json", body) 35 | defer req.Body.Close() 36 | if err != nil { 37 | contextutils.LoggerFrom(ctx).Errorw("Notifying slack failed", zap.Error(err)) 38 | return 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /versionutils/kubeapi/list_test.go: -------------------------------------------------------------------------------- 1 | package kubeapi_test 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "time" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | "github.com/wx-chevalier/go-utils/versionutils/kubeapi" 11 | ) 12 | 13 | var _ = Describe("VersionList", func() { 14 | Describe("sort", func() { 15 | It("works", func() { 16 | orderedVersions := []string{"v1alpha1", "v1beta1", "v1beta2", "v1", "v2beta1", "v2beta2", "v4", "v5alpha2", "v5beta1"} 17 | 18 | subject := make(kubeapi.VersionList, 0, len(orderedVersions)) 19 | for _, v := range orderedVersions { 20 | parsedVersion, err := kubeapi.ParseVersion(v) 21 | Expect(err).NotTo(HaveOccurred()) 22 | subject = append(subject, parsedVersion) 23 | } 24 | rand.Seed(time.Now().UnixNano()) 25 | rand.Shuffle(subject.Len(), subject.Swap) 26 | 27 | sort.Slice(subject, subject.Less) 28 | 29 | for i, apiVersion := range subject { 30 | Expect(apiVersion.String()).To(Equal(orderedVersions[i])) 31 | } 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /testutils/clusterlock/clusterlock_suite_test.go: -------------------------------------------------------------------------------- 1 | package clusterlock_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wx-chevalier/go-utils/testutils/clusterlock" 7 | "github.com/wx-chevalier/go-utils/testutils/kube" 8 | "github.com/wx-chevalier/go-utils/testutils/runners/consul" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/client-go/kubernetes" 11 | 12 | . "github.com/onsi/ginkgo" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | func TestClusterlock(t *testing.T) { 17 | RegisterFailHandler(Fail) 18 | RunSpecs(t, "Clusterlock Suite") 19 | } 20 | 21 | var ( 22 | consulFactory *consul.ConsulFactory 23 | kubeClient kubernetes.Interface 24 | ) 25 | 26 | var _ = BeforeSuite(func() { 27 | kubeClient = kube.MustKubeClient() 28 | var err error 29 | consulFactory, err = consul.NewConsulFactory() 30 | Expect(err).NotTo(HaveOccurred()) 31 | }) 32 | 33 | var _ = AfterSuite(func() { 34 | _ = consulFactory.Clean() 35 | kubeClient.CoreV1().ConfigMaps("default").Delete(clusterlock.LockResourceName, &v1.DeleteOptions{}) 36 | }) 37 | -------------------------------------------------------------------------------- /dockerutils/exec.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "os/exec" 7 | ) 8 | 9 | // containerCmd implements exec.Cmd for docker containers 10 | type containerCmd struct { 11 | nameOrID string // the container name or ID 12 | args []string 13 | env []string 14 | stdin io.Reader 15 | stdout io.Writer 16 | stderr io.Writer 17 | } 18 | 19 | func Command(args ...string) *containerCmd { 20 | return &containerCmd{ 21 | args: args, 22 | stderr: os.Stderr, 23 | stdout: os.Stdout, 24 | } 25 | } 26 | 27 | func (c *containerCmd) Run() error { 28 | var args []string 29 | 30 | // set env 31 | for _, env := range c.env { 32 | args = append(args, "-e", env) 33 | } 34 | args = append( 35 | args, 36 | // finally, with the caller args 37 | c.args..., 38 | ) 39 | 40 | cmd := exec.Command("docker", args...) 41 | if c.stdin != nil { 42 | cmd.Stdin = c.stdin 43 | } 44 | if c.stderr != nil { 45 | cmd.Stderr = c.stderr 46 | } 47 | if c.stdout != nil { 48 | cmd.Stdout = c.stdout 49 | } 50 | return cmd.Run() 51 | } 52 | -------------------------------------------------------------------------------- /kubectrlutils/pod_termination.go: -------------------------------------------------------------------------------- 1 | package kubeutils 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/wx-chevalier/go-utils/log" 7 | ) 8 | 9 | const ( 10 | TERMINATION_LOG = "/dev/termination-log" 11 | ) 12 | 13 | /* 14 | This function is in kubeutils because it is meant to only be used in containers deployed in kubernetes 15 | 16 | Upon termination kubernetes pods will read from a log file in the container and output the contents to 17 | the pod spec for debugging. https://kubernetes.io/docs/tasks/debug-application-cluster/determine-reason-pod-failure/ 18 | */ 19 | 20 | func LogFailureState(failureErr error) { 21 | file, err := os.OpenFile(TERMINATION_LOG, os.O_RDWR, 0) 22 | if err != nil { 23 | // termination log file does not exist - this can happen in non-kube environments so it is a no-op 24 | return 25 | } 26 | _, err = file.Write([]byte(failureErr.Error())) 27 | if err != nil { 28 | // we failed to write to termination log, this should never happen 29 | log.Fatalf("failed to write error %s due to %s", failureErr.Error(), err.Error()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /maputils/maputil.go: -------------------------------------------------------------------------------- 1 | package maputil 2 | 3 | // KeysOfStringInterfaceMap ... 4 | func KeysOfStringInterfaceMap(m map[string]interface{}) []string { 5 | keys := make([]string, len(m)) 6 | 7 | i := 0 8 | for k := range m { 9 | keys[i] = k 10 | i++ 11 | } 12 | 13 | return keys 14 | } 15 | 16 | // KeysOfStringStringMap ... 17 | func KeysOfStringStringMap(m map[string]string) []string { 18 | keys := make([]string, len(m)) 19 | 20 | i := 0 21 | for k := range m { 22 | keys[i] = k 23 | i++ 24 | } 25 | 26 | return keys 27 | } 28 | 29 | // CloneStringStringMap ... 30 | func CloneStringStringMap(m map[string]string) map[string]string { 31 | cloneMap := map[string]string{} 32 | for k, v := range m { 33 | cloneMap[k] = v 34 | } 35 | return cloneMap 36 | } 37 | 38 | // MergeStringStringMap - returns a new map object, 39 | // DOES NOT modify either input map 40 | func MergeStringStringMap(m1, m2 map[string]string) map[string]string { 41 | mergedMap := CloneStringStringMap(m1) 42 | for k, v := range m2 { 43 | mergedMap[k] = v 44 | } 45 | return mergedMap 46 | } 47 | -------------------------------------------------------------------------------- /manifestutils/README.md: -------------------------------------------------------------------------------- 1 | # Manifest Test Library 2 | 3 | This package contains some utils for easily parsing installation manifests (i.e. Helm-generated yaml files) 4 | and writing tests against those manifests. This is to ensure your Helm chart is properly linted and meets 5 | expectations. 6 | 7 | ## Usage 8 | 9 | - Define a test suite that parses a manifest, for example see `test/example_suite_test.go`. 10 | - Define a test file that tests the manifest, for example see `test/example_test.go`. 11 | 12 | These tests should be very concise based on the resource builder utilities provided. 13 | 14 | ## Future 15 | 16 | In the short term, the goal is increasing test coverage of helm charts that are published by different 17 | projects. Some useful extensions in the future would include: 18 | 19 | - Extra utilities for helping test helm charts with different values overrides. 20 | 21 | It may make sense to turn this into a Helm chart generator in the future, to the extent that process can be 22 | simplified (see `github.com/wx-chevalier/build` for more info about the Solo common build SDK). -------------------------------------------------------------------------------- /logutils/log_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestSetOutWriter(t *testing.T) { 12 | var b bytes.Buffer 13 | SetOutWriter(&b) 14 | Printf("test %s", "log") 15 | require.Equal(t, "test log\n", b.String()) 16 | } 17 | 18 | func TestSetEnableDebugLog(t *testing.T) { 19 | t.Log("enable debug log") 20 | { 21 | SetEnableDebugLog(true) 22 | var b bytes.Buffer 23 | SetOutWriter(&b) 24 | Debugf("test %s", "log") 25 | require.Equal(t, "\x1b[35;1mtest log\x1b[0m\n", b.String()) 26 | } 27 | 28 | t.Log("disable debug log") 29 | { 30 | SetEnableDebugLog(false) 31 | var b bytes.Buffer 32 | SetOutWriter(&b) 33 | Debugf("test %s", "log") 34 | require.Equal(t, "", b.String()) 35 | } 36 | } 37 | 38 | func TestSetTimestampLayout(t *testing.T) { 39 | var b bytes.Buffer 40 | SetOutWriter(&b) 41 | SetTimestampLayout("15-04-05") 42 | TPrintf("test %s", "log") 43 | re := regexp.MustCompile(`\[.+-.+-.+\] test log`) 44 | require.True(t, re.MatchString(b.String()), b.String()) 45 | } 46 | -------------------------------------------------------------------------------- /testutils/helper/http_echo.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type echoPod struct { 8 | *testContainer 9 | } 10 | 11 | func (t *echoPod) Deploy(timeout time.Duration) error { 12 | return t.deploy(timeout) 13 | } 14 | 15 | const ( 16 | defaultHttpEchoImage = "kennship/http-echo:latest" 17 | HttpEchoName = "http-echo" 18 | HttpEchoPort = 3000 19 | ) 20 | 21 | func NewEchoHttp(namespace string) (*echoPod, error) { 22 | container, err := newTestContainer(namespace, defaultHttpEchoImage, HttpEchoName, HttpEchoPort) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return &echoPod{ 27 | testContainer: container, 28 | }, nil 29 | } 30 | 31 | const ( 32 | defaultTcpEchoImage = "soloio/tcp-echo:latest" 33 | TcpEchoName = "tcp-echo" 34 | TcpEchoPort = 1025 35 | ) 36 | 37 | func NewEchoTcp(namespace string) (*echoPod, error) { 38 | container, err := newTestContainer(namespace, defaultTcpEchoImage, TcpEchoName, TcpEchoPort) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return &echoPod{ 43 | testContainer: container, 44 | }, nil 45 | } 46 | -------------------------------------------------------------------------------- /logutils/severity.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "github.com/wx-chevalier/go-utils/colorstring" 4 | 5 | // Severity ... 6 | type Severity uint8 7 | 8 | const ( 9 | errorSeverity Severity = iota 10 | warnSeverity 11 | normalSeverity 12 | infoSeverity 13 | successSeverity 14 | debugSeverity 15 | ) 16 | 17 | type severityColorFunc colorstring.ColorfFunc 18 | 19 | var ( 20 | successSeverityColorFunc severityColorFunc = colorstring.Greenf 21 | infoSeverityColorFunc severityColorFunc = colorstring.Bluef 22 | normalSeverityColorFunc severityColorFunc = colorstring.NoColorf 23 | debugSeverityColorFunc severityColorFunc = colorstring.Magentaf 24 | warnSeverityColorFunc severityColorFunc = colorstring.Yellowf 25 | errorSeverityColorFunc severityColorFunc = colorstring.Redf 26 | ) 27 | 28 | var severityColorFuncMap = map[Severity]severityColorFunc{ 29 | successSeverity: successSeverityColorFunc, 30 | infoSeverity: infoSeverityColorFunc, 31 | normalSeverity: normalSeverityColorFunc, 32 | debugSeverity: debugSeverityColorFunc, 33 | warnSeverity: warnSeverityColorFunc, 34 | errorSeverity: errorSeverityColorFunc, 35 | } 36 | -------------------------------------------------------------------------------- /wmanager/README.md: -------------------------------------------------------------------------------- 1 | # wmanager 2 | 3 | manage worker in golang, provide start, stop, pname method for worker. 4 | 5 | wmanager add trycatch wrapper for each worker. 6 | 7 | ## Usage 8 | 9 | ### 1. Your method implements this interface 10 | 11 | ```go 12 | type Worker interface { 13 | Start() // block mode 14 | Stop() // only call worker ctx cancel 15 | Pname() string // worker process name 16 | Status() int // worker status 17 | } 18 | ``` 19 | 20 | ### 2. new woker manager, add worker 21 | 22 | ```go 23 | wm = wmanager.NewWorkerManager() 24 | 25 | // server 26 | wm.AddWorker(master.NewAPIServer(ctx)) 27 | wm.AddWorker(master.NewServer(ctx)) 28 | 29 | // worker 30 | wm.AddWorker(master.NewHrkeScheduler(ctx)) 31 | wm.AddWorker(master.NewCollector(ctx)) 32 | ``` 33 | 34 | ### 3. start workers 35 | 36 | ```go 37 | wm.Start() 38 | ``` 39 | 40 | ### 4. stop workers 41 | 42 | ```golang 43 | wm.Stop() 44 | ``` 45 | 46 | ### 5. stop workers 47 | 48 | ```golang 49 | wm.Stop() 50 | ``` 51 | 52 | ### 6. wait for all worker exit 53 | 54 | ```golang 55 | wm.Wait() 56 | 57 | or 58 | 59 | wm.WaitTimeout(N) 60 | ``` 61 | -------------------------------------------------------------------------------- /vfsutils/mount_test.go: -------------------------------------------------------------------------------- 1 | package vfsutils_test 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wx-chevalier/go-utils/githubutils" 7 | "github.com/wx-chevalier/go-utils/vfsutils" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = Describe("mounted repo utils", func() { 14 | 15 | const ( 16 | owner = "wx-chevalier" 17 | repo = "testrepo" 18 | sha = "9065a9a84e286ea7f067f4fc240944b0a4d4c82a" 19 | ) 20 | 21 | var ( 22 | ctx = context.Background() 23 | mountedRepo vfsutils.MountedRepo 24 | ) 25 | 26 | BeforeEach(func() { 27 | client, err := githubutils.GetClient(ctx) 28 | Expect(err).NotTo(HaveOccurred()) 29 | mountedRepo = vfsutils.NewLazilyMountedRepo(client, owner, repo, sha) 30 | }) 31 | 32 | It("can get contents", func() { 33 | contents, err := mountedRepo.GetFileContents(ctx, "tmp.txt") 34 | Expect(err).NotTo(HaveOccurred()) 35 | Expect(string(contents)).To(ContainSubstring("another")) 36 | }) 37 | 38 | It("can list files", func() { 39 | files, err := mountedRepo.ListFiles(ctx, "namespace") 40 | Expect(err).NotTo(HaveOccurred()) 41 | Expect(len(files)).To(Equal(1)) 42 | }) 43 | 44 | }) 45 | -------------------------------------------------------------------------------- /funcutils/retry.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // Action ... 9 | type Action func(attempt uint) error 10 | 11 | // Model ... 12 | type Model struct { 13 | retry uint 14 | waitTime time.Duration 15 | } 16 | 17 | // Times ... 18 | func Times(retry uint) *Model { 19 | Model := Model{} 20 | return Model.Times(retry) 21 | } 22 | 23 | // Times ... 24 | func (Model *Model) Times(retry uint) *Model { 25 | Model.retry = retry 26 | return Model 27 | } 28 | 29 | // Wait ... 30 | func Wait(waitTime time.Duration) *Model { 31 | Model := Model{} 32 | return Model.Wait(waitTime) 33 | } 34 | 35 | // Wait ... 36 | func (Model *Model) Wait(waitTime time.Duration) *Model { 37 | Model.waitTime = waitTime 38 | return Model 39 | } 40 | 41 | // Try ... 42 | func (Model Model) Try(action Action) error { 43 | if action == nil { 44 | return fmt.Errorf("no action specified") 45 | } 46 | 47 | var err error 48 | for attempt := uint(0); (0 == attempt || nil != err) && attempt <= Model.retry; attempt++ { 49 | if attempt > 0 && Model.waitTime > 0 { 50 | time.Sleep(Model.waitTime) 51 | } 52 | 53 | err = action(attempt) 54 | } 55 | 56 | return err 57 | } 58 | -------------------------------------------------------------------------------- /botutils/interface.go: -------------------------------------------------------------------------------- 1 | package botutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/google/go-github/github" 7 | ) 8 | 9 | type Plugin interface { 10 | } 11 | 12 | type PullRequestHandler interface { 13 | Plugin 14 | HandlePREvent(ctx context.Context, client *github.Client, event *github.PullRequestEvent) error 15 | } 16 | 17 | type PullRequestReviewHandler interface { 18 | Plugin 19 | HandlePullRequestReviewEvent(ctx context.Context, client *github.Client, event *github.PullRequestReviewEvent) error 20 | } 21 | 22 | type IssueCommentHandler interface { 23 | Plugin 24 | HandleIssueCommentEvent(ctx context.Context, client *github.Client, event *github.IssueCommentEvent) error 25 | } 26 | 27 | type CommitCommentHandler interface { 28 | Plugin 29 | HandleCommitCommentEvent(ctx context.Context, client *github.Client, event *github.CommitCommentEvent) error 30 | } 31 | 32 | type ReleaseHandler interface { 33 | Plugin 34 | HandleReleaseEvent(ctx context.Context, client *github.Client, event *github.ReleaseEvent) error 35 | } 36 | 37 | type IssuesHandler interface { 38 | Plugin 39 | HandleIssuesEvent(ctx context.Context, client *github.Client, event *github.IssuesEvent) error 40 | } 41 | -------------------------------------------------------------------------------- /commandutils/command_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestNewCommandSlice(t *testing.T) { 10 | t.Log("it fails if slice empty") 11 | { 12 | cmd, err := NewFromSlice([]string{}) 13 | require.Error(t, err) 14 | require.Equal(t, (*Model)(nil), cmd) 15 | } 16 | 17 | t.Log("it creates cmd if cmdSlice has 1 element") 18 | { 19 | _, err := NewFromSlice([]string{"ls"}) 20 | require.NoError(t, err) 21 | } 22 | 23 | t.Log("it creates cmd if cmdSlice has multiple elements") 24 | { 25 | _, err := NewFromSlice([]string{"ls", "-a", "-l", "-h"}) 26 | require.NoError(t, err) 27 | } 28 | } 29 | 30 | func TestNewWithParams(t *testing.T) { 31 | t.Log("it fails if params empty") 32 | { 33 | cmd, err := NewWithParams() 34 | require.Error(t, err) 35 | require.Equal(t, (*Model)(nil), cmd) 36 | } 37 | 38 | t.Log("it creates cmd if params has 1 element") 39 | { 40 | _, err := NewWithParams("ls") 41 | require.NoError(t, err) 42 | } 43 | 44 | t.Log("it creates cmd if params has multiple elements") 45 | { 46 | _, err := NewWithParams("ls", "-a", "-l", "-h") 47 | require.NoError(t, err) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /healthutils/grpc.go: -------------------------------------------------------------------------------- 1 | package healthchecker 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "sync/atomic" 7 | "syscall" 8 | 9 | "google.golang.org/grpc/health" 10 | healthpb "google.golang.org/grpc/health/grpc_health_v1" 11 | ) 12 | 13 | type grpcHealthChecker struct { 14 | srv *health.Server 15 | ok uint32 16 | name string 17 | } 18 | 19 | var _ HealthChecker = new(grpcHealthChecker) 20 | 21 | func NewGrpc(serviceName string, grpcHealthServer *health.Server) *grpcHealthChecker { 22 | hc := &grpcHealthChecker{} 23 | hc.ok = 1 24 | hc.name = serviceName 25 | 26 | hc.srv = grpcHealthServer 27 | hc.srv.SetServingStatus(serviceName, healthpb.HealthCheckResponse_SERVING) 28 | 29 | sigterm := make(chan os.Signal, 1) 30 | signal.Notify(sigterm, syscall.SIGTERM) 31 | 32 | go func() { 33 | <-sigterm 34 | atomic.StoreUint32(&hc.ok, 0) 35 | hc.srv.SetServingStatus(serviceName, healthpb.HealthCheckResponse_NOT_SERVING) 36 | }() 37 | 38 | return hc 39 | } 40 | 41 | func (hc *grpcHealthChecker) Fail() { 42 | atomic.StoreUint32(&hc.ok, 0) 43 | hc.srv.SetServingStatus(hc.name, healthpb.HealthCheckResponse_NOT_SERVING) 44 | } 45 | 46 | func (hc *grpcHealthChecker) GetServer() *health.Server { 47 | return hc.srv 48 | } 49 | -------------------------------------------------------------------------------- /kubectrlutils/wait_crd.go: -------------------------------------------------------------------------------- 1 | package kubeutils 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/avast/retry-go" 7 | "github.com/wx-chevalier/go-utils/errutils" 8 | "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 9 | apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | // Waits for a CRD to be "established" in kubernetes, which means it's active an can be 14 | // CRUD'ed by clients 15 | func WaitForCrdActive(apiexts apiexts.Interface, crdName string) error { 16 | return retry.Do(func() error { 17 | crd, err := apiexts.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crdName, metav1.GetOptions{}) 18 | if err != nil { 19 | return errors.Wrapf(err, "lookup crd %v", crdName) 20 | } 21 | 22 | var established bool 23 | for _, status := range crd.Status.Conditions { 24 | if status.Type == v1beta1.Established { 25 | established = true 26 | break 27 | } 28 | } 29 | 30 | if !established { 31 | return errors.Errorf("crd %v exists but not yet established by kube", crdName) 32 | } 33 | 34 | return nil 35 | }, 36 | retry.Delay(time.Millisecond*500), 37 | retry.DelayType(retry.FixedDelay), 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /strutils/text.go: -------------------------------------------------------------------------------- 1 | package stringutil 2 | 3 | import ( 4 | "bufio" 5 | "math" 6 | "strings" 7 | ) 8 | 9 | // IndentTextWithMaxLength ... 10 | func IndentTextWithMaxLength(text, indent string, maxTextLineCharWidth int, isIndentFirstLine bool) string { 11 | if maxTextLineCharWidth < 1 { 12 | return "" 13 | } 14 | 15 | formattedText := "" 16 | 17 | addLine := func(line string) { 18 | isFirstLine := (formattedText == "") 19 | if isFirstLine && !isIndentFirstLine { 20 | formattedText = line 21 | } else { 22 | if !isFirstLine { 23 | formattedText += "\n" 24 | } 25 | formattedText += indent + line 26 | } 27 | } 28 | 29 | scanner := bufio.NewScanner(strings.NewReader(text)) 30 | for scanner.Scan() { 31 | line := scanner.Text() 32 | lineLength := len(line) 33 | if lineLength > maxTextLineCharWidth { 34 | lineCnt := math.Ceil(float64(lineLength) / float64(maxTextLineCharWidth)) 35 | for i := 0; i < int(lineCnt); i++ { 36 | startIdx := i * maxTextLineCharWidth 37 | endIdx := startIdx + maxTextLineCharWidth 38 | if endIdx > lineLength { 39 | endIdx = lineLength 40 | } 41 | addLine(line[startIdx:endIdx]) 42 | } 43 | } else { 44 | addLine(line) 45 | } 46 | } 47 | 48 | return formattedText 49 | } 50 | -------------------------------------------------------------------------------- /kubeinstallutils/kubeinstall/mocks/mock_kube_installer.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wx-chevalier/go-utils/installutils/kubeinstall" 7 | "github.com/wx-chevalier/go-utils/installutils/kuberesource" 8 | ) 9 | 10 | type MockKubeInstaller struct { 11 | ReconcileCalledWith ReconcileParams 12 | PurgeCalledWith PurgeParams 13 | ReturnErr error 14 | } 15 | 16 | type ReconcileParams struct { 17 | InstallNamespace string 18 | Resources kuberesource.UnstructuredResources 19 | InstallLabels map[string]string 20 | } 21 | 22 | type PurgeParams struct { 23 | InstallLabels map[string]string 24 | } 25 | 26 | func (i *MockKubeInstaller) ReconcileResources(ctx context.Context, params kubeinstall.ReconcileParams) error { 27 | i.ReconcileCalledWith = ReconcileParams{params.InstallNamespace, params.Resources, params.OwnerLabels} 28 | return i.ReturnErr 29 | } 30 | 31 | func (i *MockKubeInstaller) PurgeResources(ctx context.Context, withLabels map[string]string) error { 32 | i.PurgeCalledWith = PurgeParams{withLabels} 33 | return i.ReturnErr 34 | } 35 | 36 | func (i *MockKubeInstaller) ListAllResources(ctx context.Context) kuberesource.UnstructuredResources { 37 | return i.ReconcileCalledWith.Resources 38 | } 39 | -------------------------------------------------------------------------------- /testutils/assertions.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "github.com/gogo/protobuf/proto" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | // ExpectEqualProtoMessages provides richer error messages than struct comparison by leveraging the String() method that all 9 | // proto Messages provide. On error, Gomega's string comparison utility prints a few characters of the text immediately 10 | // surrounding the first discrepancy. 11 | // 12 | // Variadic optionalDescription argument is passed on to fmt.Sprintf() and is used to annotate failure messages. 13 | // 14 | // Example of the output: 15 | // optionalDescription is rendered here: template string with values foo and bar 16 | // Expected 17 | // : "...-1010" vers..." 18 | // to equal | 19 | // : "...-10101" ver..." 20 | func ExpectEqualProtoMessages(a, b proto.Message, optionalDescription ...interface{}) { 21 | if proto.Equal(a, b) { 22 | return 23 | } 24 | // One shortcoming is that you only get +/- 5 chars of context 25 | // per: https://github.com/onsi/gomega/blob/master/format/format.go#L146 26 | // TODO(mitchdraft) gomega pr to modify charactersAroundMismatchToInclude (if not merged will make a util) 27 | Expect(a.String()).To(Equal(b.String()), optionalDescription...) 28 | } 29 | -------------------------------------------------------------------------------- /testutils/deploy_testrunner.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "k8s.io/client-go/rest" 5 | ) 6 | 7 | // Deprecated: no one calls this 8 | func DeployTestRunner(cfg *rest.Config, namespace string) error { 9 | return DeployFromYaml(cfg, namespace, TestRunnerYaml) 10 | } 11 | 12 | const TestRunnerYaml = ` 13 | apiVersion: v1 14 | kind: Pod 15 | metadata: 16 | labels: 17 | gloo: testrunner 18 | name: testrunner 19 | spec: 20 | containers: 21 | - image: soloio/testrunner:testing-8671e8b9 22 | imagePullPolicy: IfNotPresent 23 | command: 24 | - sleep 25 | - "36000" 26 | name: testrunner 27 | restartPolicy: Always` 28 | 29 | const NginxYaml = ` 30 | apiVersion: apps/v1 31 | kind: Deployment 32 | metadata: 33 | name: nginx-deployment 34 | spec: 35 | selector: 36 | matchLabels: 37 | app: nginx 38 | replicas: 2 39 | template: 40 | metadata: 41 | labels: 42 | app: nginx 43 | spec: 44 | containers: 45 | - name: nginx 46 | image: nginx:1.7.9 47 | ports: 48 | - containerPort: 80 49 | --- 50 | apiVersion: v1 51 | kind: Service 52 | metadata: 53 | name: nginx 54 | labels: 55 | app: nginx 56 | spec: 57 | ports: 58 | - port: 80 59 | name: http 60 | selector: 61 | app: nginx 62 | ` 63 | -------------------------------------------------------------------------------- /kubectrlutils/namespaces.go: -------------------------------------------------------------------------------- 1 | package kubeutils 2 | 3 | import ( 4 | "golang.org/x/sync/errgroup" 5 | v1 "k8s.io/api/core/v1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | func CreateNamespacesInParallel(kube kubernetes.Interface, namespaces ...string) error { 11 | eg := errgroup.Group{} 12 | for _, namespace := range namespaces { 13 | namespace := namespace 14 | eg.Go(func() error { 15 | _, err := kube.CoreV1().Namespaces().Create(&v1.Namespace{ 16 | ObjectMeta: metav1.ObjectMeta{ 17 | Name: namespace, 18 | }, 19 | }) 20 | return err 21 | }) 22 | } 23 | return eg.Wait() 24 | } 25 | 26 | func DeleteNamespacesInParallelBlocking(kube kubernetes.Interface, namespaces ...string) error { 27 | eg := errgroup.Group{} 28 | for _, namespace := range namespaces { 29 | namespace := namespace 30 | eg.Go(func() error { 31 | return kube.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{}) 32 | }) 33 | } 34 | return eg.Wait() 35 | } 36 | 37 | func DeleteNamespacesInParallel(kube kubernetes.Interface, namespaces ...string) { 38 | for _, namespace := range namespaces { 39 | namespace := namespace 40 | go func() { 41 | kube.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{}) 42 | }() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /envutils/vars.go: -------------------------------------------------------------------------------- 1 | package envutils 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "strconv" 7 | 8 | "go.uber.org/zap" 9 | 10 | "github.com/wx-chevalier/go-utils/contextutils" 11 | ) 12 | 13 | func MustGetPodNamespace(ctx context.Context) string { 14 | contextutils.LoggerFrom(ctx).Infow("Looking for install namespace in POD_NAMESPACE environment variable") 15 | namespace := os.Getenv("POD_NAMESPACE") 16 | if namespace == "" { 17 | contextutils.LoggerFrom(ctx).Fatalw("Could not determine namespace, must have non-empty POD_NAMESPACE in environment") 18 | } 19 | contextutils.LoggerFrom(ctx).Infow("Found install namespace", zap.String("installNamespace", namespace)) 20 | return namespace 21 | } 22 | 23 | func MustGetGrpcPort(ctx context.Context) int { 24 | contextutils.LoggerFrom(ctx).Infow("Looking for grpc port in GRPC_PORT environment variable") 25 | port := os.Getenv("GRPC_PORT") 26 | if port == "" { 27 | contextutils.LoggerFrom(ctx).Fatalw("Could not determine port, must have non-empty GRPC_PORT in environment") 28 | } 29 | p, err := strconv.Atoi(port) 30 | if err != nil { 31 | contextutils.LoggerFrom(ctx).Fatalw("Failed to parse grpc port", 32 | zap.Error(err), 33 | zap.String("port", port)) 34 | } 35 | contextutils.LoggerFrom(ctx).Infow("Found grpc port", zap.Int("grpcPort", p)) 36 | return p 37 | } 38 | -------------------------------------------------------------------------------- /strutils/strings.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | ) 7 | 8 | func ContainsString(s string, slice []string) bool { 9 | for _, s2 := range slice { 10 | if s2 == s { 11 | return true 12 | } 13 | } 14 | return false 15 | } 16 | 17 | // Returns true if slice strings contains any of the strings. 18 | func ContainsAny(strings []string, slice []string) bool { 19 | for _, s := range strings { 20 | if ContainsString(s, slice) { 21 | return true 22 | } 23 | } 24 | return false 25 | } 26 | 27 | func ContainsMap(maps []map[string]string, item map[string]string) bool { 28 | for _, m := range maps { 29 | if reflect.DeepEqual(m, item) { 30 | return true 31 | } 32 | } 33 | return false 34 | } 35 | 36 | func KeysAndValues(m map[string]string) ([]string, []string) { 37 | var keys []string 38 | for k := range m { 39 | keys = append(keys, k) 40 | } 41 | sort.Strings(keys) 42 | var values []string 43 | for _, k := range keys { 44 | values = append(values, m[k]) 45 | } 46 | return keys, values 47 | } 48 | 49 | func Unique(stringSlice []string) []string { 50 | keys := make(map[string]bool) 51 | list := []string{} 52 | for _, entry := range stringSlice { 53 | if _, value := keys[entry]; !value { 54 | keys[entry] = true 55 | list = append(list, entry) 56 | } 57 | } 58 | return list 59 | } 60 | -------------------------------------------------------------------------------- /progressutils/wrapper.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "golang.org/x/crypto/ssh/terminal" 9 | ) 10 | 11 | // Wrapper ... 12 | type Wrapper struct { 13 | spinner Spinner 14 | action func() 15 | interactiveMode bool 16 | } 17 | 18 | // NewWrapper ... 19 | func NewWrapper(spinner Spinner, interactiveMode bool) Wrapper { 20 | return Wrapper{ 21 | spinner: spinner, 22 | interactiveMode: interactiveMode, 23 | } 24 | } 25 | 26 | // NewDefaultWrapper ... 27 | func NewDefaultWrapper(message string) Wrapper { 28 | spinner := NewDefaultSpinner(message) 29 | interactiveMode := OutputDeviceIsTerminal() 30 | return NewWrapper(spinner, interactiveMode) 31 | } 32 | 33 | // WrapAction ... 34 | func (w Wrapper) WrapAction(action func()) { 35 | if w.interactiveMode { 36 | w.spinner.Start() 37 | action() 38 | w.spinner.Stop() 39 | } else { 40 | message := w.spinner.message 41 | if !strings.HasSuffix(message, ".") { 42 | message = message + "..." 43 | } 44 | if _, err := fmt.Fprintln(w.spinner.writer, message); err != nil { 45 | fmt.Printf("failed to print message: %s, error: %s", message, err) 46 | } 47 | action() 48 | } 49 | } 50 | 51 | // OutputDeviceIsTerminal ... 52 | func OutputDeviceIsTerminal() bool { 53 | return terminal.IsTerminal(int(os.Stdout.Fd())) 54 | } 55 | -------------------------------------------------------------------------------- /testutils/exec/cmd.go: -------------------------------------------------------------------------------- 1 | package exec 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "os/exec" 8 | 9 | "github.com/onsi/ginkgo" 10 | "github.com/wx-chevalier/go-utils/errutils" 11 | ) 12 | 13 | func RunCommand(workingDir string, verbose bool, args ...string) error { 14 | _, err := RunCommandOutput(workingDir, verbose, args...) 15 | return err 16 | } 17 | 18 | func RunCommandOutput(workingDir string, verbose bool, args ...string) (string, error) { 19 | return RunCommandInputOutput("", workingDir, verbose, args...) 20 | } 21 | 22 | func RunCommandInput(input, workingDir string, verbose bool, args ...string) error { 23 | _, err := RunCommandInputOutput(input, workingDir, verbose, args...) 24 | return err 25 | } 26 | 27 | func RunCommandInputOutput(input, workingDir string, verbose bool, args ...string) (string, error) { 28 | cmd := exec.Command(args[0], args[1:]...) 29 | cmd.Dir = workingDir 30 | cmd.Env = os.Environ() 31 | if len(input) > 0 { 32 | cmd.Stdin = bytes.NewBuffer([]byte(input)) 33 | } 34 | buf := &bytes.Buffer{} 35 | var out io.Writer 36 | if verbose { 37 | out = io.MultiWriter(buf, ginkgo.GinkgoWriter) 38 | } else { 39 | out = buf 40 | } 41 | cmd.Stdout = out 42 | cmd.Stderr = out 43 | if err := cmd.Run(); err != nil { 44 | return "", errors.Wrapf(err, "%v failed: %s", cmd.Args, buf.String()) 45 | } 46 | 47 | return buf.String(), nil 48 | } 49 | -------------------------------------------------------------------------------- /testutils/testutil.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/wx-chevalier/go-utils/builtinutil" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | // EqualAndNoError ... 12 | func EqualAndNoError(t *testing.T, expected, actual interface{}, err error, msgAndArgs ...interface{}) { 13 | require.NoError(t, err, msgAndArgs...) 14 | require.Equal(t, expected, actual, msgAndArgs...) 15 | } 16 | 17 | func equalSlicesWithoutOrder(t *testing.T, expected, actual []interface{}, msgAndArgs ...interface{}) { 18 | if !builtinutil.DeepEqualSlices(expected, actual) { 19 | require.FailNow(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ 20 | " != %#v (actual)", expected, actual), msgAndArgs...) 21 | } 22 | } 23 | 24 | // EqualSlicesWithoutOrder - regardless the order, but same items 25 | func EqualSlicesWithoutOrder(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) { 26 | castedExpected, err := builtinutil.CastInterfaceToInterfaceSlice(expected) 27 | if err != nil { 28 | require.FailNow(t, fmt.Sprintf("'expected' is not a slice: %#v", expected), msgAndArgs...) 29 | } 30 | 31 | castedActual, err := builtinutil.CastInterfaceToInterfaceSlice(actual) 32 | if err != nil { 33 | require.FailNow(t, fmt.Sprintf("'actual' is not a slice: %#v", actual), msgAndArgs...) 34 | } 35 | 36 | equalSlicesWithoutOrder(t, castedExpected, castedActual, msgAndArgs...) 37 | } 38 | -------------------------------------------------------------------------------- /logutils/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "regexp" 8 | "runtime" 9 | "time" 10 | 11 | "github.com/k0kubun/pp" 12 | ) 13 | 14 | var debugMode = os.Getenv("DEBUG") == "1" 15 | var DefaultOut io.Writer = os.Stdout 16 | 17 | func init() { 18 | if os.Getenv("DISABLE_COLOR") == "1" { 19 | pp.ColoringEnabled = false 20 | } 21 | } 22 | 23 | var rxp = regexp.MustCompile(".*/src/") 24 | 25 | func Sprintf(format string, a ...interface{}) string { 26 | return pp.Sprintf("%v\t"+format, append([]interface{}{line()}, a...)...) 27 | } 28 | 29 | func GreyPrintf(format string, a ...interface{}) { 30 | fmt.Fprintf(DefaultOut, "%v\t"+format+"\n", append([]interface{}{line()}, a...)...) 31 | } 32 | 33 | func Printf(format string, a ...interface{}) { 34 | fmt.Fprintln(DefaultOut, Sprintf(format, a...)) 35 | } 36 | 37 | func Warnf(format string, a ...interface{}) { 38 | fmt.Fprintln(DefaultOut, Sprintf("WARNING:\n"+format+"\n", a...)) 39 | } 40 | 41 | func Debugf(format string, a ...interface{}) { 42 | if debugMode { 43 | fmt.Fprintln(DefaultOut, Sprintf(format, a...)) 44 | } 45 | } 46 | 47 | func Fatalf(format string, a ...interface{}) { 48 | fmt.Fprintln(DefaultOut, Sprintf(format, a...)) 49 | os.Exit(1) 50 | } 51 | 52 | func line() string { 53 | _, file, line, _ := runtime.Caller(3) 54 | file = rxp.ReplaceAllString(file, "") 55 | return fmt.Sprintf("%v: %v:%v", time.Now().Format(time.RFC1123), file, line) 56 | } 57 | -------------------------------------------------------------------------------- /pkcs12utils/README.md: -------------------------------------------------------------------------------- 1 | # package pkcs12 2 | 3 | [![GoDoc](https://godoc.org/software.sslmate.com/src/go-pkcs12?status.svg)](https://godoc.org/software.sslmate.com/src/go-pkcs12) 4 | 5 | import "software.sslmate.com/src/go-pkcs12" 6 | 7 | Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). 8 | It is intended for decoding P12/PFX files for use with the `crypto/tls` 9 | package, and for encoding P12/PFX files for use by legacy applications which 10 | do not support newer formats. Since PKCS#12 uses weak encryption 11 | primitives, it SHOULD NOT be used for new applications. 12 | 13 | This package is forked from `golang.org/x/crypto/pkcs12`, which is frozen. 14 | The implementation is distilled from https://tools.ietf.org/html/rfc7292 15 | and referenced documents. 16 | 17 | This repository holds supplementary Go cryptography libraries. 18 | 19 | ## Import Path 20 | 21 | Note that although the source code and issue tracker for this package are hosted 22 | on GitHub, the import path is: 23 | 24 | software.sslmate.com/src/go-pkcs12 25 | 26 | Please be sure to use this path when you `go get` and `import` this package. 27 | 28 | ## Download/Install 29 | 30 | The easiest way to install is to run `go get -u software.sslmate.com/src/go-pkcs12`. You 31 | can also manually git clone the repository to `$GOPATH/src/software.sslmate.com/src/go-pkcs12`. 32 | 33 | ## Report Issues / Send Patches 34 | 35 | Open an issue or PR at https://github.com/SSLMate/go-pkcs12 36 | -------------------------------------------------------------------------------- /contextutils/errors_and_logging.go: -------------------------------------------------------------------------------- 1 | package contextutils 2 | 3 | import ( 4 | "context" 5 | 6 | "go.uber.org/zap" 7 | ) 8 | 9 | func SilenceLogger(ctx context.Context) context.Context { 10 | return withLogger(ctx, zap.NewNop().Sugar()) 11 | } 12 | 13 | func WithLogger(ctx context.Context, name string) context.Context { 14 | return withLogger(ctx, fromContext(ctx).Named(name)) 15 | } 16 | 17 | func WithLoggerValues(ctx context.Context, meta ...interface{}) context.Context { 18 | return withLogger(ctx, fromContext(ctx).With(meta...)) 19 | } 20 | 21 | func LoggerFrom(ctx context.Context) *zap.SugaredLogger { 22 | return fromContext(ctx) 23 | } 24 | 25 | type ErrorHandler interface { 26 | HandleErr(error) 27 | } 28 | 29 | type ErrorLogger struct { 30 | ctx context.Context 31 | } 32 | 33 | func (h *ErrorLogger) HandleErr(err error) { 34 | if err == nil { 35 | return 36 | } 37 | fromContext(h.ctx).Errorf(err.Error()) 38 | } 39 | 40 | type errorHandlerKey struct{} 41 | 42 | func WithErrorHandler(ctx context.Context, errorHandler ErrorHandler) context.Context { 43 | return context.WithValue(ctx, errorHandlerKey{}, errorHandler) 44 | } 45 | 46 | func ErrorHandlerFrom(ctx context.Context) ErrorHandler { 47 | val := ctx.Value(errorHandlerKey{}) 48 | if val == nil { 49 | return &ErrorLogger{ 50 | ctx: ctx, 51 | } 52 | } 53 | errorHandler, ok := val.(ErrorHandler) 54 | if !ok { 55 | return &ErrorLogger{ 56 | ctx: ctx, 57 | } 58 | } 59 | return errorHandler 60 | } 61 | -------------------------------------------------------------------------------- /pkcs12utils/pbkdf_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkcs12 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) { 13 | cipherInfo := shaWithTripleDESCBC{} 14 | 15 | salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff") 16 | password, _ := bmpString("sesame") 17 | key := cipherInfo.deriveKey(salt, password, 2048) 18 | 19 | if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); bytes.Compare(key, expected) != 0 { 20 | t.Fatalf("expected key '%x', but found '%x'", expected, key) 21 | } 22 | } 23 | 24 | func TestThatPBKDFHandlesLeadingZeros(t *testing.T) { 25 | // This test triggers a case where I_j (in step 6C) ends up with leading zero 26 | // byte, meaning that len(Ijb) < v (leading zeros get stripped by big.Int). 27 | // This was previously causing bug whereby certain inputs would break the 28 | // derivation and produce the wrong output. 29 | key := pbkdf(sha1Sum, 20, 64, []byte("\xf3\x7e\x05\xb5\x18\x32\x4b\x4b"), []byte("\x00\x00"), 2048, 1, 24) 30 | expected := []byte("\x00\xf7\x59\xff\x47\xd1\x4d\xd0\x36\x65\xd5\x94\x3c\xb3\xc4\xa3\x9a\x25\x55\xc0\x2a\xed\x66\xe1") 31 | if bytes.Compare(key, expected) != 0 { 32 | t.Fatalf("expected key '%x', but found '%x'", expected, key) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /debugutils/test/aggregator_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | "github.com/wx-chevalier/go-utils/debugutils" 7 | "github.com/wx-chevalier/go-utils/tarutils" 8 | "github.com/spf13/afero" 9 | ) 10 | 11 | var _ = Describe("aggregator test", func() { 12 | var ( 13 | aggregator *debugutils.Aggregator 14 | fs afero.Fs 15 | ) 16 | 17 | Context("e2e", func() { 18 | BeforeEach(func() { 19 | fs = afero.NewOsFs() 20 | storageClient := debugutils.NewFileStorageClient(fs) 21 | logCollector, err := debugutils.DefaultLogCollector() 22 | Expect(err).NotTo(HaveOccurred()) 23 | resourceCollector, err := debugutils.DefaultResourceCollector() 24 | Expect(err).NotTo(HaveOccurred()) 25 | tmpd, err := afero.TempDir(fs, "", "") 26 | Expect(err).NotTo(HaveOccurred()) 27 | aggregator = debugutils.NewAggregator(resourceCollector, logCollector, storageClient, fs, tmpd) 28 | }) 29 | It("can properly tar up all resources", func() { 30 | tmpf, err := afero.TempFile(fs, "", "") 31 | defer fs.Remove(tmpf.Name()) 32 | Expect(err).NotTo(HaveOccurred()) 33 | err = aggregator.StreamFromManifest(manifests, "gloo-system", tmpf.Name()) 34 | Expect(err).NotTo(HaveOccurred()) 35 | tmpd, err := afero.TempDir(fs, "", "") 36 | Expect(err).NotTo(HaveOccurred()) 37 | defer fs.RemoveAll(tmpd) 38 | err = tarutils.Untar(tmpd, tmpf.Name(), fs) 39 | Expect(err).NotTo(HaveOccurred()) 40 | }) 41 | }) 42 | 43 | }) 44 | -------------------------------------------------------------------------------- /logutils/defaultlogger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | // DefaultLogger ... 4 | type DefaultLogger struct { 5 | ts bool 6 | } 7 | 8 | // NewDefaultLogger ... 9 | func NewDefaultLogger(withTimestamp bool) DefaultLogger { 10 | return DefaultLogger{withTimestamp} 11 | } 12 | 13 | // Donef ... 14 | func (dl DefaultLogger) Donef(format string, v ...interface{}) { 15 | fSelect(dl.ts, TDonef, Donef)(format, v...) 16 | } 17 | 18 | // Successf ... 19 | func (dl DefaultLogger) Successf(format string, v ...interface{}) { 20 | fSelect(dl.ts, TSuccessf, Successf)(format, v...) 21 | } 22 | 23 | // Infof ... 24 | func (dl DefaultLogger) Infof(format string, v ...interface{}) { 25 | fSelect(dl.ts, TInfof, Infof)(format, v...) 26 | } 27 | 28 | // Printf ... 29 | func (dl DefaultLogger) Printf(format string, v ...interface{}) { 30 | fSelect(dl.ts, TPrintf, Printf)(format, v...) 31 | } 32 | 33 | // Warnf ... 34 | func (dl DefaultLogger) Warnf(format string, v ...interface{}) { 35 | fSelect(dl.ts, TWarnf, Warnf)(format, v...) 36 | } 37 | 38 | // Errorf ... 39 | func (dl DefaultLogger) Errorf(format string, v ...interface{}) { 40 | fSelect(dl.ts, TErrorf, Errorf)(format, v...) 41 | } 42 | 43 | // Debugf ... 44 | func (dl DefaultLogger) Debugf(format string, v ...interface{}) { 45 | if enableDebugLog { 46 | fSelect(dl.ts, TDebugf, Debugf)(format, v...) 47 | } 48 | } 49 | 50 | type logfunc func(string, ...interface{}) 51 | 52 | func fSelect(t bool, tf logfunc, f logfunc) logfunc { 53 | if t { 54 | return tf 55 | } 56 | return f 57 | } 58 | -------------------------------------------------------------------------------- /strutils/url.go: -------------------------------------------------------------------------------- 1 | package urlutil 2 | 3 | import ( 4 | "errors" 5 | "net/url" 6 | "strings" 7 | ) 8 | 9 | // Join ... 10 | func Join(elems ...string) (string, error) { 11 | if len(elems) < 1 { 12 | return "", errors.New("No elements defined to Join") 13 | } 14 | 15 | url, err := url.Parse(elems[0]) 16 | if err != nil { 17 | return "", err 18 | } 19 | schemeStr := url.Scheme 20 | if schemeStr == "" { 21 | return "", errors.New("No Scheme defined") 22 | } 23 | 24 | hostStr := url.Host 25 | if hostStr == "" { 26 | return "", errors.New("No Host defined") 27 | } 28 | 29 | pathStr := "" 30 | if url.Path != "" { 31 | pathStr = clearPrefix(url.Path) 32 | pathStr = clearSuffix(pathStr) 33 | } 34 | 35 | combined := schemeStr + "://" + hostStr 36 | if pathStr != "" { 37 | combined = combined + "/" + pathStr 38 | } 39 | 40 | for i, e := range elems { 41 | if i > 0 { 42 | if i < len(elems)-1 { 43 | pathStr = clearPrefix(e) 44 | pathStr = clearSuffix(pathStr) 45 | 46 | combined = combined + "/" + pathStr 47 | } else { 48 | pathStr = clearPrefix(e) 49 | combined = combined + "/" + pathStr 50 | } 51 | } 52 | } 53 | 54 | return combined, nil 55 | } 56 | 57 | func clearPrefix(s string) string { 58 | if strings.HasPrefix(s, "/") { 59 | s = s[1:len(s)] 60 | s = clearPrefix(s) 61 | } 62 | return s 63 | } 64 | 65 | func clearSuffix(s string) string { 66 | if strings.HasSuffix(s, "/") { 67 | s = s[0 : len(s)-1] 68 | s = clearSuffix(s) 69 | } 70 | return s 71 | } 72 | -------------------------------------------------------------------------------- /kubectrlutils/wait_crd_test.go: -------------------------------------------------------------------------------- 1 | package kubeutils_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | . "github.com/wx-chevalier/go-utils/kubeutils" 7 | "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 8 | apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | var _ = Describe("WaitCrd", func() { 13 | var ( 14 | api apiexts.Interface 15 | crdName = "testing" 16 | ) 17 | BeforeEach(func() { 18 | cfg, err := GetConfig("", "") 19 | Expect(err).NotTo(HaveOccurred()) 20 | api, err = apiexts.NewForConfig(cfg) 21 | Expect(err).NotTo(HaveOccurred()) 22 | crd, err := api.ApiextensionsV1beta1().CustomResourceDefinitions().Create(&v1beta1.CustomResourceDefinition{ 23 | ObjectMeta: v1.ObjectMeta{Name: "somethings.test.solo.io"}, 24 | Spec: v1beta1.CustomResourceDefinitionSpec{ 25 | Group: "test.solo.io", 26 | Names: v1beta1.CustomResourceDefinitionNames{ 27 | Plural: "somethings", 28 | Kind: "Something", 29 | ShortNames: []string{"st"}, 30 | }, 31 | Version: "v1", 32 | }, 33 | }) 34 | Expect(err).NotTo(HaveOccurred()) 35 | crdName = crd.Name 36 | }) 37 | AfterEach(func() { 38 | api.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(crdName, nil) 39 | }) 40 | It("waits successfully for a crd to become established", func() { 41 | err := WaitForCrdActive(api, crdName) 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /strutils/colorstring_test.go: -------------------------------------------------------------------------------- 1 | package colorstring 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestAddColor(t *testing.T) { 11 | /* 12 | blackColor Color = "\x1b[30;1m" 13 | resetColor Color = "\x1b[0m" 14 | */ 15 | 16 | t.Log("colored_string = color + string + reset_color") 17 | { 18 | desiredColored := "\x1b[30;1m" + "test" + "\x1b[0m" 19 | colored := addColor(blackColor, "test") 20 | require.Equal(t, desiredColored, colored) 21 | } 22 | } 23 | 24 | func TestBlack(t *testing.T) { 25 | t.Log("Simple string can be blacked") 26 | { 27 | desiredColored := "\x1b[30;1m" + "test" + "\x1b[0m" 28 | colored := Black("test") 29 | require.Equal(t, desiredColored, colored) 30 | } 31 | 32 | t.Log("Multiple strings can be blacked") 33 | { 34 | desiredColored := "\x1b[30;1m" + "Hello Bitrise !" + "\x1b[0m" 35 | colored := Black("Hello ", "Bitrise ", "!") 36 | require.Equal(t, desiredColored, colored) 37 | } 38 | } 39 | 40 | func TestBlackf(t *testing.T) { 41 | t.Log("Simple format can be blacked") 42 | { 43 | desiredColored := "\x1b[30;1m" + fmt.Sprintf("Hello %s", "bitrise") + "\x1b[0m" 44 | colored := Blackf("Hello %s", "bitrise") 45 | require.Equal(t, desiredColored, colored) 46 | } 47 | 48 | t.Log("Complex format can be blacked") 49 | { 50 | desiredColored := "\x1b[30;1m" + fmt.Sprintf("Hello %s %s", "bitrise", "!") + "\x1b[0m" 51 | colored := Blackf("Hello %s %s", "bitrise", "!") 52 | require.Equal(t, desiredColored, colored) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /grpcx/limiters.go: -------------------------------------------------------------------------------- 1 | package grpcx 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | var ( 12 | DefaultRateLimiter = NewRateLimiterPool(50, 100) 13 | 14 | // todo: clean 15 | defaultCleanInterval = 6 * time.Hour 16 | ) 17 | 18 | type RateLimiterPool struct { 19 | r rate.Limit 20 | b int 21 | limiters map[string]*rate.Limiter 22 | cleanInterval time.Duration 23 | 24 | sync.RWMutex 25 | } 26 | 27 | func NewRateLimiterPool(r rate.Limit, b int) *RateLimiterPool { 28 | l := &RateLimiterPool{ 29 | r: r, 30 | b: b, 31 | limiters: make(map[string]*rate.Limiter), 32 | } 33 | 34 | return l 35 | } 36 | 37 | func (l *RateLimiterPool) AddLimiter(tag string) *rate.Limiter { 38 | l.Lock() 39 | defer l.Unlock() 40 | 41 | return l.addLimiter(tag) 42 | } 43 | 44 | func (l *RateLimiterPool) addLimiter(tag string) *rate.Limiter { 45 | limiter := rate.NewLimiter(l.r, l.b) 46 | l.limiters[tag] = limiter 47 | 48 | return limiter 49 | } 50 | 51 | func (l *RateLimiterPool) GetLimiter(tag string) *rate.Limiter { 52 | l.Lock() 53 | defer l.Unlock() 54 | 55 | limiter, exists := l.limiters[tag] 56 | if !exists { 57 | return l.addLimiter(tag) 58 | } 59 | 60 | return limiter 61 | } 62 | 63 | func (l *RateLimiterPool) Allow(tag string) bool { 64 | limiter := l.GetLimiter(tag) 65 | return limiter.Allow() 66 | } 67 | 68 | func (l *RateLimiterPool) Wait(ctx context.Context, tag string) error { 69 | limiter := l.GetLimiter(tag) 70 | return limiter.Wait(ctx) 71 | } 72 | -------------------------------------------------------------------------------- /errutils/errors.go: -------------------------------------------------------------------------------- 1 | package errutils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os/exec" 7 | "regexp" 8 | "strings" 9 | "syscall" 10 | 11 | "golang.org/x/xerrors" 12 | ) 13 | 14 | func Wrapf(err error, format string, args ...interface{}) error { 15 | errString := fmt.Sprintf(format, args...) 16 | return xerrors.Errorf("%s: %w", errString, err) 17 | } 18 | 19 | func Errorf(format string, args ...interface{}) error { 20 | return xerrors.Errorf(format, args...) 21 | } 22 | 23 | func Errors(msgs []string) error { 24 | return xerrors.Errorf(strings.Join(msgs, "\n")) 25 | } 26 | 27 | func New(text string) error { 28 | return xerrors.New(text) 29 | } 30 | 31 | func Is(err, target error) bool { 32 | return xerrors.Is(err, target) 33 | } 34 | 35 | // IsExitStatusError ... 36 | func IsExitStatusError(err error) bool { 37 | return IsExitStatusErrorStr(err.Error()) 38 | } 39 | 40 | // IsExitStatusErrorStr ... 41 | func IsExitStatusErrorStr(errString string) bool { 42 | // example exit status error string: exit status 1 43 | var rex = regexp.MustCompile(`^exit status [0-9]{1,3}$`) 44 | return rex.MatchString(errString) 45 | } 46 | 47 | // CmdExitCodeFromError ... 48 | func CmdExitCodeFromError(err error) (int, error) { 49 | cmdExitCode := 0 50 | if err != nil { 51 | if exitError, ok := err.(*exec.ExitError); ok { 52 | waitStatus, ok := exitError.Sys().(syscall.WaitStatus) 53 | if !ok { 54 | return 1, errors.New("Failed to cast exit status") 55 | } 56 | cmdExitCode = waitStatus.ExitStatus() 57 | } 58 | return cmdExitCode, nil 59 | } 60 | return 0, nil 61 | } 62 | -------------------------------------------------------------------------------- /kubectrlutils/util.go: -------------------------------------------------------------------------------- 1 | package kubeutils 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // use SanitizeNameV2 10 | // DEPRECATED 11 | func SanitizeName(name string) string { 12 | name = strings.Replace(name, "*", "-", -1) 13 | name = strings.Replace(name, "/", "-", -1) 14 | name = strings.Replace(name, ".", "-", -1) 15 | name = strings.Replace(name, "[", "", -1) 16 | name = strings.Replace(name, "]", "", -1) 17 | name = strings.Replace(name, ":", "-", -1) 18 | name = strings.Replace(name, " ", "-", -1) 19 | name = strings.Replace(name, "\n", "", -1) 20 | if len(name) > 63 { 21 | hash := md5.Sum([]byte(name)) 22 | name = fmt.Sprintf("%s-%x", name[:31], hash) 23 | name = name[:63] 24 | } 25 | name = strings.Replace(name, ".", "-", -1) 26 | name = strings.ToLower(name) 27 | return name 28 | } 29 | 30 | func SanitizeNameV2(name string) string { 31 | name = strings.Replace(name, "*", "-", -1) 32 | name = strings.Replace(name, "/", "-", -1) 33 | name = strings.Replace(name, ".", "-", -1) 34 | name = strings.Replace(name, "[", "", -1) 35 | name = strings.Replace(name, "]", "", -1) 36 | name = strings.Replace(name, ":", "-", -1) 37 | name = strings.Replace(name, " ", "-", -1) 38 | name = strings.Replace(name, "\n", "", -1) 39 | name = strings.Replace(name, "\"", "", -1) 40 | name = strings.Replace(name, "'", "", -1) 41 | if len(name) > 63 { 42 | hash := md5.Sum([]byte(name)) 43 | name = fmt.Sprintf("%s-%x", name[:31], hash) 44 | name = name[:63] 45 | } 46 | name = strings.Replace(name, ".", "-", -1) 47 | name = strings.ToLower(name) 48 | return name 49 | } 50 | -------------------------------------------------------------------------------- /dockerutils/pull.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/wx-chevalier/go-utils/contextutils" 8 | "github.com/wx-chevalier/go-utils/errutils" 9 | ) 10 | 11 | // PullIfNotPresent will pull an image if it is not present locally 12 | // retrying up to retries times 13 | // it returns true if it attempted to pull, and any errors from pulling 14 | func PullIfNotPresent(ctx context.Context, image string, retries int) (pulled bool, err error) { 15 | logger := contextutils.LoggerFrom(ctx) 16 | // if this did not return an error, then the image exists locally 17 | cmd := Command("inspect", "--type=image", image) 18 | if err := cmd.Run(); err == nil { 19 | logger.Infof("Image: %s present locally", image) 20 | return false, nil 21 | } 22 | // otherwise try to pull it 23 | return true, Pull(ctx, image, retries) 24 | } 25 | 26 | // Pull pulls an image, retrying up to retries times 27 | func Pull(ctx context.Context, image string, retries int) error { 28 | logger := contextutils.LoggerFrom(ctx) 29 | logger.Infof("Pulling image: %s ...", image) 30 | err := Command("pull", image).Run() 31 | // retry pulling up to retries times if necessary 32 | if err != nil { 33 | for i := 0; i < retries; i++ { 34 | time.Sleep(time.Second * time.Duration(i+1)) 35 | logger.Warnf(errors.Wrapf(err, "Trying again to pull image: %s ...", image).Error()) 36 | err = Command("pull", image).Run() 37 | if err == nil { 38 | break 39 | } 40 | } 41 | } 42 | if err != nil { 43 | logger.Warnf(errors.Wrapf(err, "Failed to pull image: %s", image).Error()) 44 | } 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /stats/README.md: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | enable debug logs with 4 | 5 | ```shell 6 | curl -XPUT -d '{"level":"debug"}' -H"content-type: application/json" http://localhost:9091/logging 7 | ``` 8 | 9 | # zPages 10 | 11 | see them here: 12 | 13 | localhost:9091/zpages/tracez 14 | 15 | # Local Prometheus 16 | 17 | Start like so: 18 | 19 | ```shell 20 | cat < config.yaml 21 | global: 22 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 23 | 24 | # A scrape configuration containing exactly one endpoint to scrape: 25 | scrape_configs: 26 | # The job name is added as a label 'job=' to any timeseries scraped from this config. 27 | - job_name: 'go-utils' 28 | 29 | # Override the global default and scrape targets from this job every 1 seconds. 30 | scrape_interval: 1s 31 | 32 | static_configs: 33 | - targets: ['localhost:9091'] 34 | EOF 35 | prometheus --config.file=./config.yaml 36 | ``` 37 | 38 | Prometheus will be available in `localhost:9090`. 39 | 40 | To see the rate of incoming snapshots, try this query: 41 | 42 | ``` 43 | rate(api_snap_emitter_snap_in[5m]) 44 | ``` 45 | 46 | # Profiling 47 | 48 | ## CPU tracing 49 | 50 | ``` 51 | $ curl http://localhost:9091/debug/pprof/trace?seconds=5 -o trace.out 52 | $ go tool trace trace.out 53 | ``` 54 | 55 | ## profiling 56 | 57 | ``` 58 | $ curl http://localhost:9091/debug/pprof/profile?seconds=5 -o pprof.out 59 | $ go tool pprof pprof.out 60 | (pprof) top 61 | (pprof) web 62 | ``` 63 | 64 | ## Stack traces 65 | 66 | See go routines: 67 | 68 | ``` 69 | $ curl http://localhost:9091/debug/pprof/goroutine?debug=2 70 | ``` 71 | -------------------------------------------------------------------------------- /kubesetuputils/crd.go: -------------------------------------------------------------------------------- 1 | package kubeinstallutils 2 | 3 | import ( 4 | "github.com/wx-chevalier/go-utils/errutils" 5 | "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 6 | apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 7 | apierrors "k8s.io/apimachinery/pkg/api/errors" 8 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | func CrdsFromManifest(crdManifestYaml string) ([]*v1beta1.CustomResourceDefinition, error) { 12 | var crds []*v1beta1.CustomResourceDefinition 13 | crdRuntimeObjects, err := ParseKubeManifest(crdManifestYaml) 14 | if err != nil { 15 | return nil, err 16 | } 17 | for _, obj := range crdRuntimeObjects { 18 | apiExtCrd, ok := obj.(*v1beta1.CustomResourceDefinition) 19 | if !ok { 20 | return nil, errors.Wrapf(err, "internal error: crd manifest must only contain CustomResourceDefinitions") 21 | } 22 | crds = append(crds, apiExtCrd) 23 | } 24 | return crds, nil 25 | } 26 | 27 | func CreateCrds(apiExts apiexts.Interface, crds ...*v1beta1.CustomResourceDefinition) error { 28 | for _, crd := range crds { 29 | if _, err := apiExts.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd); err != nil && !apierrors.IsAlreadyExists(err) { 30 | return errors.Wrapf(err, "failed to create crd: %v", crd) 31 | } 32 | } 33 | return nil 34 | } 35 | 36 | func DeleteCrds(apiExts apiexts.Interface, crdNames ...string) error { 37 | for _, name := range crdNames { 38 | err := apiExts.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(name, &v1.DeleteOptions{}) 39 | if err != nil && !apierrors.IsNotFound(err) { 40 | return errors.Wrapf(err, "failed to delete crd: %v", name) 41 | } 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /kubeinstallutils/manifests.go: -------------------------------------------------------------------------------- 1 | package installutils 2 | 3 | import ( 4 | "path/filepath" 5 | "regexp" 6 | "strings" 7 | 8 | "helm.sh/helm/v3/pkg/releaseutil" 9 | 10 | "github.com/wx-chevalier/go-utils/vfsutils" 11 | "github.com/spf13/afero" 12 | ) 13 | 14 | var ( 15 | kindRegex = regexp.MustCompile("kind:(.*)\n") 16 | ) 17 | 18 | func GetManifestsFromRemoteTar(tarUrl string) ([]releaseutil.Manifest, error) { 19 | fs := afero.NewMemMapFs() 20 | dir, err := vfsutils.MountTar(fs, tarUrl) 21 | if err != nil { 22 | return nil, err 23 | } 24 | files, err := afero.ReadDir(fs, dir) 25 | if err != nil { 26 | return nil, err 27 | } 28 | templates := make(map[string]string) 29 | for _, file := range files { 30 | filename := filepath.Join(dir, file.Name()) 31 | contents, err := afero.ReadFile(fs, filename) 32 | if err != nil { 33 | return nil, err 34 | } 35 | templates[filename] = string(contents) 36 | } 37 | return SplitManifests(templates), nil 38 | } 39 | 40 | // SplitManifests takes a map of rendered templates and splits them into the 41 | // detected manifests. 42 | // (ported from Helm 2: https://github.com/helm/helm/blob/release-2.16/pkg/manifest/splitter.go) 43 | func SplitManifests(templates map[string]string) []releaseutil.Manifest { 44 | var listManifests []releaseutil.Manifest 45 | // extract kind and name 46 | for k, v := range templates { 47 | match := kindRegex.FindStringSubmatch(v) 48 | h := "Unknown" 49 | if len(match) == 2 { 50 | h = strings.TrimSpace(match[1]) 51 | } 52 | m := releaseutil.Manifest{Name: k, Content: v, Head: &releaseutil.SimpleHead{Kind: h}} 53 | listManifests = append(listManifests, m) 54 | } 55 | 56 | return listManifests 57 | } 58 | -------------------------------------------------------------------------------- /dockerutils/docker_test.go: -------------------------------------------------------------------------------- 1 | package docker_test 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" 6 | "os/exec" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | "github.com/wx-chevalier/go-utils/docker" 11 | ) 12 | 13 | const ( 14 | validImage = "soloio/gloo:0.7.0" 15 | invalidImage = "soloio/gloo:0.1.0" 16 | ) 17 | 18 | var _ = BeforeSuite(func() { 19 | exec.Command("docker", "image", "rm", validImage).Run() 20 | }) 21 | 22 | var _ = Describe("Docker", func() { 23 | 24 | pullValidImage := func() bool { 25 | ok, err := docker.PullIfNotPresent(context.TODO(), validImage, 1) 26 | Expect(err).NotTo(HaveOccurred()) 27 | return ok 28 | } 29 | 30 | pullInvalidImage := func() { 31 | _, err := docker.PullIfNotPresent(context.TODO(), invalidImage, 1) 32 | Expect(err).To(HaveOccurred()) 33 | } 34 | 35 | Context("Pull", func() { 36 | It("can pull a valid container", func() { 37 | Expect(pullValidImage()).To(Equal(true)) 38 | Expect(pullValidImage()).To(Equal(false)) 39 | }) 40 | 41 | It("cannot pull an invalid container", func() { 42 | pullInvalidImage() 43 | }) 44 | 45 | }) 46 | 47 | Context("Save", func() { 48 | It("can save a valid, present container", func() { 49 | pullValidImage() 50 | file, err := ioutil.TempFile("", "docker_test") 51 | Expect(err).NotTo(HaveOccurred()) 52 | err = docker.Save(validImage, file.Name()) 53 | Expect(err).NotTo(HaveOccurred()) 54 | }) 55 | 56 | It("cannot save an invalid container", func() { 57 | file, err := ioutil.TempFile("", "docker_test") 58 | Expect(err).NotTo(HaveOccurred()) 59 | err = docker.Save(invalidImage, file.Name()) 60 | Expect(err).To(HaveOccurred()) 61 | }) 62 | }) 63 | 64 | }) 65 | -------------------------------------------------------------------------------- /pkcs12utils/bmp-string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkcs12 6 | 7 | import ( 8 | "errors" 9 | "unicode/utf16" 10 | ) 11 | 12 | // bmpString returns s encoded in UCS-2 with a zero terminator. 13 | func bmpString(s string) ([]byte, error) { 14 | // References: 15 | // https://tools.ietf.org/html/rfc7292#appendix-B.1 16 | // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane 17 | // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes 18 | // EncodeRune returns 0xfffd if the rune does not need special encoding 19 | // - the above RFC provides the info that BMPStrings are NULL terminated. 20 | 21 | ret := make([]byte, 0, 2*len(s)+2) 22 | 23 | for _, r := range s { 24 | if t, _ := utf16.EncodeRune(r); t != 0xfffd { 25 | return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") 26 | } 27 | ret = append(ret, byte(r/256), byte(r%256)) 28 | } 29 | 30 | return append(ret, 0, 0), nil 31 | } 32 | 33 | func decodeBMPString(bmpString []byte) (string, error) { 34 | if len(bmpString)%2 != 0 { 35 | return "", errors.New("pkcs12: odd-length BMP string") 36 | } 37 | 38 | // strip terminator if present 39 | if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { 40 | bmpString = bmpString[:l-2] 41 | } 42 | 43 | s := make([]uint16, 0, len(bmpString)/2) 44 | for len(bmpString) > 0 { 45 | s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) 46 | bmpString = bmpString[2:] 47 | } 48 | 49 | return string(utf16.Decode(s)), nil 50 | } 51 | -------------------------------------------------------------------------------- /logutils/print_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func Test_printf_with_time(t *testing.T) { 12 | SetTimestampLayout("15.04.05") 13 | var b bytes.Buffer 14 | SetOutWriter(&b) 15 | printf(normalSeverity, true, "test %s", "log") 16 | re := regexp.MustCompile(`\[.+\..+\..+\] test log`) 17 | require.True(t, re.MatchString(b.String()), b.String()) 18 | } 19 | 20 | func Test_printf_severity(t *testing.T) { 21 | t.Log("error") 22 | { 23 | var b bytes.Buffer 24 | SetOutWriter(&b) 25 | printf(errorSeverity, false, "test %s", "log") 26 | require.Equal(t, "\x1b[31;1mtest log\x1b[0m\n", b.String()) 27 | } 28 | 29 | t.Log("warn") 30 | { 31 | var b bytes.Buffer 32 | SetOutWriter(&b) 33 | printf(warnSeverity, false, "test %s", "log") 34 | require.Equal(t, "\x1b[33;1mtest log\x1b[0m\n", b.String()) 35 | } 36 | 37 | t.Log("debug") 38 | { 39 | var b bytes.Buffer 40 | SetOutWriter(&b) 41 | printf(debugSeverity, false, "test %s", "log") 42 | require.Equal(t, "\x1b[35;1mtest log\x1b[0m\n", b.String()) 43 | } 44 | 45 | t.Log("normal") 46 | { 47 | var b bytes.Buffer 48 | SetOutWriter(&b) 49 | printf(normalSeverity, false, "test %s", "log") 50 | require.Equal(t, "test log\n", b.String()) 51 | } 52 | 53 | t.Log("info") 54 | { 55 | var b bytes.Buffer 56 | SetOutWriter(&b) 57 | printf(infoSeverity, false, "test %s", "log") 58 | require.Equal(t, "\x1b[34;1mtest log\x1b[0m\n", b.String()) 59 | } 60 | 61 | t.Log("success") 62 | { 63 | var b bytes.Buffer 64 | SetOutWriter(&b) 65 | printf(successSeverity, false, "test %s", "log") 66 | require.Equal(t, "\x1b[32;1mtest log\x1b[0m\n", b.String()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pkcs12utils/mac.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. 2 | // Copyright 2015 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package pkcs12 7 | 8 | import ( 9 | "crypto/hmac" 10 | "crypto/sha1" 11 | "crypto/x509/pkix" 12 | "encoding/asn1" 13 | ) 14 | 15 | type macData struct { 16 | Mac digestInfo 17 | MacSalt []byte 18 | Iterations int `asn1:"optional,default:1"` 19 | } 20 | 21 | // from PKCS#7: 22 | type digestInfo struct { 23 | Algorithm pkix.AlgorithmIdentifier 24 | Digest []byte 25 | } 26 | 27 | var ( 28 | oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) 29 | ) 30 | 31 | func verifyMac(macData *macData, message, password []byte) error { 32 | if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { 33 | return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) 34 | } 35 | 36 | key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) 37 | 38 | mac := hmac.New(sha1.New, key) 39 | mac.Write(message) 40 | expectedMAC := mac.Sum(nil) 41 | 42 | if !hmac.Equal(macData.Mac.Digest, expectedMAC) { 43 | return ErrIncorrectPassword 44 | } 45 | return nil 46 | } 47 | 48 | func computeMac(macData *macData, message, password []byte) error { 49 | if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { 50 | return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) 51 | } 52 | 53 | key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) 54 | 55 | mac := hmac.New(sha1.New, key) 56 | mac.Write(message) 57 | macData.Mac.Digest = mac.Sum(nil) 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /errutils/errors_test.go: -------------------------------------------------------------------------------- 1 | package errutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestIsExitStatusErrorStr(t *testing.T) { 10 | // --- Should match --- 11 | require.Equal(t, true, IsExitStatusErrorStr("exit status 1")) 12 | require.Equal(t, true, IsExitStatusErrorStr("exit status 0")) 13 | require.Equal(t, true, IsExitStatusErrorStr("exit status 2")) 14 | require.Equal(t, true, IsExitStatusErrorStr("exit status 11")) 15 | require.Equal(t, true, IsExitStatusErrorStr("exit status 111")) 16 | require.Equal(t, true, IsExitStatusErrorStr("exit status 999")) 17 | 18 | // --- Should not match --- 19 | require.Equal(t, false, IsExitStatusErrorStr("xit status 1")) 20 | require.Equal(t, false, IsExitStatusErrorStr("status 1")) 21 | require.Equal(t, false, IsExitStatusErrorStr("exit status ")) 22 | require.Equal(t, false, IsExitStatusErrorStr("exit status")) 23 | require.Equal(t, false, IsExitStatusErrorStr("exit status 2112")) 24 | require.Equal(t, false, IsExitStatusErrorStr("exit status 21121")) 25 | 26 | // prefixed 27 | require.Equal(t, false, IsExitStatusErrorStr(".exit status 1")) 28 | require.Equal(t, false, IsExitStatusErrorStr(" exit status 1")) 29 | require.Equal(t, false, IsExitStatusErrorStr("error: exit status 1")) 30 | // postfixed 31 | require.Equal(t, false, IsExitStatusErrorStr("exit status 1.")) 32 | require.Equal(t, false, IsExitStatusErrorStr("exit status 1 ")) 33 | require.Equal(t, false, IsExitStatusErrorStr("exit status 1 - something else")) 34 | require.Equal(t, false, IsExitStatusErrorStr("exit status 1 2")) 35 | 36 | // other 37 | require.Equal(t, false, IsExitStatusErrorStr("-exit status 211-")) 38 | require.Equal(t, false, IsExitStatusErrorStr("something else: exit status 1")) 39 | } 40 | -------------------------------------------------------------------------------- /testutils/fail_handler.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "os/signal" 8 | "runtime/debug" 9 | "strings" 10 | "syscall" 11 | 12 | . "github.com/onsi/ginkgo" 13 | . "github.com/onsi/gomega" 14 | ) 15 | 16 | func waitOnFail() { 17 | 18 | if os.Getenv("WAIT_ON_FAIL") == "0" { 19 | return 20 | } 21 | 22 | if os.Getenv("WAIT_ON_FAIL") == "1" || IsDebuggerPresent() { 23 | // wait for sig usr1 24 | c := make(chan os.Signal, 1) 25 | signal.Notify(c, syscall.SIGUSR1) 26 | defer signal.Reset(syscall.SIGUSR1) 27 | fmt.Println("We are here:") 28 | debug.PrintStack() 29 | fmt.Printf("Waiting for human intervention. to continue, run 'kill -SIGUSR1 %d'\n", os.Getpid()) 30 | <-c 31 | } 32 | } 33 | 34 | var preFails []func() 35 | 36 | func RegisterPreFailHandler(prefail func()) { 37 | preFails = append(preFails, prefail) 38 | } 39 | 40 | func RegisterCommonFailHandlers() { 41 | RegisterPreFailHandler(waitOnFail) 42 | RegisterFailHandler(failHandler) 43 | } 44 | 45 | func failHandler(message string, callerSkip ...int) { 46 | fmt.Println("Fail handler msg", message) 47 | 48 | for _, prefail := range preFails { 49 | prefail() 50 | } 51 | Fail(message, callerSkip...) 52 | 53 | } 54 | 55 | func IsDebuggerPresent() bool { 56 | f, err := ioutil.ReadFile("/proc/self/status") 57 | if err != nil { 58 | // no status so we don't know 59 | return false 60 | } 61 | status := string(f) 62 | if !strings.Contains(status, "TracerPid:") { 63 | // no tracer pid field, so we don't know 64 | return false 65 | } 66 | 67 | if strings.Contains(status, "TracerPid:\t0") { 68 | // no tracer pid - no debugger 69 | return false 70 | } 71 | // tracer pid is present and not zero - we have a debugger 72 | return true 73 | } 74 | -------------------------------------------------------------------------------- /fileutils/file_crud.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strings" 7 | 8 | command "github.com/wx-chevalier/go-utils/commandutils" 9 | ) 10 | 11 | // CopyFile ... 12 | func CopyFile(src, dst string) error { 13 | // replace with a pure Go implementation? 14 | // Golang proposal was: https://go-review.googlesource.com/#/c/1591/5/src/io/ioutil/ioutil.go 15 | isDir, err := IsDirExists(src) 16 | if err != nil { 17 | return err 18 | } 19 | if isDir { 20 | return errors.New("Source is a directory: " + src) 21 | } 22 | args := []string{src, dst} 23 | return command.RunCommand("rsync", args...) 24 | } 25 | 26 | // CopyDir ... 27 | func CopyDir(src, dst string, isOnlyContent bool) error { 28 | if isOnlyContent && !strings.HasSuffix(src, "/") { 29 | src = src + "/" 30 | } 31 | args := []string{"-ar", src, dst} 32 | return command.RunCommand("rsync", args...) 33 | } 34 | 35 | // RemoveDir ... 36 | // Deprecated: use RemoveAll instead. 37 | func RemoveDir(dirPth string) error { 38 | if exist, err := IsPathExists(dirPth); err != nil { 39 | return err 40 | } else if exist { 41 | if err := os.RemoveAll(dirPth); err != nil { 42 | return err 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | // RemoveFile ... 49 | // Deprecated: use RemoveAll instead. 50 | func RemoveFile(pth string) error { 51 | if exist, err := IsPathExists(pth); err != nil { 52 | return err 53 | } else if exist { 54 | if err := os.Remove(pth); err != nil { 55 | return err 56 | } 57 | } 58 | return nil 59 | } 60 | 61 | // RemoveAll removes recursively every file on the given paths. 62 | func RemoveAll(pths ...string) error { 63 | for _, pth := range pths { 64 | if err := os.RemoveAll(pth); err != nil { 65 | return err 66 | } 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /debugutils/debugutils_suite_test.go: -------------------------------------------------------------------------------- 1 | package debugutils 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "cloud.google.com/go/storage" 9 | "github.com/golang/mock/gomock" 10 | . "github.com/onsi/ginkgo" 11 | . "github.com/onsi/gomega" 12 | "github.com/wx-chevalier/go-utils/installutils/helmchart" 13 | "github.com/wx-chevalier/go-utils/installutils/kuberesource" 14 | "google.golang.org/api/iterator" 15 | ) 16 | 17 | func TestDebugutils(t *testing.T) { 18 | T = t 19 | RegisterFailHandler(Fail) 20 | RunSpecs(t, "Debugutils Suite") 21 | } 22 | 23 | var ( 24 | T *testing.T 25 | ns string 26 | ctrl *gomock.Controller 27 | 28 | manifests helmchart.Manifests 29 | unstructuredResources kuberesource.UnstructuredResources 30 | 31 | _ = SynchronizedBeforeSuite(func() []byte { 32 | var err error 33 | manifests, err = helmchart.RenderManifests( 34 | context.TODO(), 35 | "https://storage.googleapis.com/solo-public-helm/charts/gloo-0.13.33.tgz", 36 | "", 37 | "aaa", 38 | "gloo-system", 39 | "", 40 | ) 41 | Expect(err).NotTo(HaveOccurred()) 42 | unstructuredResources, err = manifests.ResourceList() 43 | Expect(err).NotTo(HaveOccurred()) 44 | return nil 45 | }, func(data []byte) {}) 46 | 47 | _ = SynchronizedAfterSuite(func() {}, func() { 48 | ctx := context.TODO() 49 | client, err := storage.NewClient(ctx) 50 | Expect(err).NotTo(HaveOccurred()) 51 | bucket := client.Bucket("go-utils-test") 52 | obj := os.ExpandEnv("$BUILD_ID") 53 | it := bucket.Objects(ctx, &storage.Query{ 54 | Prefix: obj, 55 | }) 56 | for { 57 | objAttrs, err := it.Next() 58 | if err != nil && err == iterator.Done { 59 | break 60 | } 61 | Expect(err).NotTo(HaveOccurred()) 62 | Expect(bucket.Object(objAttrs.Name).Delete(ctx)).NotTo(HaveOccurred()) 63 | } 64 | }) 65 | ) 66 | -------------------------------------------------------------------------------- /githubutils/README.md: -------------------------------------------------------------------------------- 1 | ## Uploading Release Assets to Github 2 | 3 | To upload release assets to Github, follow these steps (requires go-utils 0.2.10+). 4 | 5 | ### Create a Go script 6 | 7 | Create a script `upload_github_release_asset.go`, like this: 8 | 9 | ```go 10 | package main 11 | 12 | import ( 13 | "github.com/wx-chevalier/go-utils/githubutils" 14 | ) 15 | 16 | func main() { 17 | assets := make([]githubutils.ReleaseAssetSpec, 2) 18 | assets[0] = githubutils.ReleaseAssetSpec{ 19 | Name: "hello", 20 | ParentPath: "_output", 21 | UploadSHA: true, 22 | } 23 | assets[1] = githubutils.ReleaseAssetSpec{ 24 | Name: "my-resource.yaml", 25 | ParentPath: "namespace", 26 | } 27 | spec := githubutils.UploadReleaseAssetSpec{ 28 | Owner: "wx-chevalier", 29 | Repo: "testrepo", 30 | Assets: assets, 31 | SkipAlreadyExists: true, 32 | } 33 | githubutils.UploadReleaseAssetCli(&spec) 34 | } 35 | ``` 36 | 37 | ### Create a Make target 38 | 39 | ```bash 40 | #---------------------------------------------------------------------------------- 41 | # Github Assets 42 | #---------------------------------------------------------------------------------- 43 | 44 | .PHONY: upload-github-release-assets 45 | upload-github-release-assets: hello 46 | go run upload_github_release_assets.go 47 | ``` 48 | 49 | ### Update cloudbuild.yaml to call this target 50 | 51 | ```yaml 52 | steps: 53 | - name: 'gcr.io/solo-corp/go-mod-make:0.1.1' 54 | args: [..., 'upload-github-release-assets', ...] 55 | secretEnv: ['GITHUB_TOKEN'] 56 | env: 57 | - 'TAGGED_VERSION=$TAG_NAME' 58 | ``` 59 | 60 | Make sure `GITHUB_TOKEN` and `TAGGED_VERSION` are in the environment. 61 | 62 | ### Notes 63 | 64 | * On each asset, a flag `UploadSHA` can be set to true to upload a SHA256 hash file. 65 | * Set `SkipAlreadyExists=true` to not fail when trying to upload an asset that already exists. -------------------------------------------------------------------------------- /fileutils/messages_test.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | 8 | "github.com/gogo/protobuf/types" 9 | "github.com/stretchr/testify/require" 10 | "github.com/stretchr/testify/suite" 11 | ) 12 | 13 | // Basic imports 14 | 15 | // Define the suite, and absorb the built-in basic suite 16 | // functionality from testify - including a T() method which 17 | // returns the current testing context 18 | type ExampleTestSuite struct { 19 | suite.Suite 20 | VariableThatShouldStartAtFive int 21 | } 22 | 23 | // Make sure that VariableThatShouldStartAtFive is set to five 24 | // before each test 25 | func (suite *ExampleTestSuite) SetupTest() { 26 | suite.VariableThatShouldStartAtFive = 5 27 | } 28 | 29 | // All methods that begin with "Test" are run as tests within a 30 | // suite. 31 | func (suite *ExampleTestSuite) TestExample() { 32 | var filename string 33 | 34 | f, err := ioutil.TempFile("", "messages_test") 35 | require.Nil(suite.T(), err) 36 | filename = f.Name() 37 | require.Contains(suite.T(), filename, "messages_test") 38 | 39 | input := &types.Struct{ 40 | Fields: map[string]*types.Value{ 41 | "foo": { 42 | Kind: &types.Value_StringValue{StringValue: "bar"}, 43 | }, 44 | }, 45 | } 46 | 47 | err = WriteToFile(filename, input) 48 | require.Nil(suite.T(), err) 49 | 50 | b, err := ioutil.ReadFile(filename) 51 | require.Nil(suite.T(), err) 52 | 53 | require.Equal(suite.T(), string(b), "foo: bar\n") 54 | 55 | var output types.Struct 56 | err = ReadFileInto(filename, &output) 57 | require.Nil(suite.T(), err) 58 | require.Equal(suite.T(), output, *input) 59 | 60 | os.RemoveAll(filename) 61 | } 62 | 63 | // In order for 'go test' to run this suite, we need to create 64 | // a normal test function and pass our suite to suite.Run 65 | func TestExampleTestSuite(t *testing.T) { 66 | suite.Run(t, new(ExampleTestSuite)) 67 | } 68 | -------------------------------------------------------------------------------- /kubectrlutils/kube_cfg.go: -------------------------------------------------------------------------------- 1 | package kubeutils 2 | 3 | import ( 4 | "os" 5 | 6 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 7 | "k8s.io/client-go/rest" 8 | "k8s.io/client-go/tools/clientcmd" 9 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 10 | ) 11 | 12 | // GetConfig gets the kubernetes client config 13 | func GetConfig(masterURL, kubeconfigPath string) (*rest.Config, error) { 14 | envVarName := clientcmd.RecommendedConfigPathEnvVar 15 | if kubeconfigPath == "" && masterURL == "" && os.Getenv(envVarName) == "" { 16 | // Neither kubeconfig nor master URL nor envVarName(KUBECONFIG) was specified. Using the inClusterConfig. 17 | kubeconfig, err := rest.InClusterConfig() 18 | if err == nil { 19 | return kubeconfig, nil 20 | } 21 | // error creating inClusterConfig, falling back to default config 22 | } 23 | 24 | if kubeconfigPath != "" { 25 | if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) { 26 | // the specified kubeconfig does not exist so fallback to default 27 | kubeconfigPath = "" 28 | } 29 | } 30 | 31 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 32 | loadingRules.ExplicitPath = kubeconfigPath 33 | configOverrides := &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterURL}} 34 | 35 | return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides).ClientConfig() 36 | } 37 | 38 | // GetConfig gets the kubernetes client config 39 | func GetKubeConfig(masterURL, kubeconfigPath string) (*clientcmdapi.Config, error) { 40 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 41 | loadingRules.ExplicitPath = kubeconfigPath 42 | configOverrides := &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterURL}} 43 | 44 | return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides).ConfigAccess().GetStartingConfig() 45 | } 46 | -------------------------------------------------------------------------------- /pkcs12utils/bmp-string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkcs12 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "testing" 11 | ) 12 | 13 | var bmpStringTests = []struct { 14 | in string 15 | expectedHex string 16 | shouldFail bool 17 | }{ 18 | {"", "0000", false}, 19 | // Example from https://tools.ietf.org/html/rfc7292#appendix-B. 20 | {"Beavis", "0042006500610076006900730000", false}, 21 | // Some characters from the "Letterlike Symbols Unicode block". 22 | {"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false}, 23 | // any character outside the BMP should trigger an error. 24 | {"\U0001f000 East wind (Mahjong)", "", true}, 25 | } 26 | 27 | func TestBMPString(t *testing.T) { 28 | for i, test := range bmpStringTests { 29 | expected, err := hex.DecodeString(test.expectedHex) 30 | if err != nil { 31 | t.Fatalf("#%d: failed to decode expectation", i) 32 | } 33 | 34 | out, err := bmpString(test.in) 35 | if err == nil && test.shouldFail { 36 | t.Errorf("#%d: expected to fail, but produced %x", i, out) 37 | continue 38 | } 39 | 40 | if err != nil && !test.shouldFail { 41 | t.Errorf("#%d: failed unexpectedly: %s", i, err) 42 | continue 43 | } 44 | 45 | if !test.shouldFail { 46 | if !bytes.Equal(out, expected) { 47 | t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out) 48 | continue 49 | } 50 | 51 | roundTrip, err := decodeBMPString(out) 52 | if err != nil { 53 | t.Errorf("#%d: decoding output gave an error: %s", i, err) 54 | continue 55 | } 56 | 57 | if roundTrip != test.in { 58 | t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, roundTrip, test.in) 59 | continue 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /debugutils/test/pods_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/ghodss/yaml" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | "github.com/wx-chevalier/go-utils/debugutils" 8 | "github.com/wx-chevalier/go-utils/installutils/kuberesource" 9 | corev1 "k8s.io/api/core/v1" 10 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | "k8s.io/client-go/kubernetes/fake" 13 | ) 14 | 15 | var _ = Describe("pod unit tests", func() { 16 | Context("Label Pod Finder", func() { 17 | var ( 18 | podFinder *debugutils.LabelPodFinder 19 | clientset *fake.Clientset 20 | ) 21 | 22 | BeforeEach(func() { 23 | clientset = fake.NewSimpleClientset(podsAsObjects(GeneratePodList())...) 24 | podFinder = debugutils.NewLabelPodFinder(clientset) 25 | }) 26 | 27 | It("can handle full use case", func() { 28 | resources, err := manifests.ResourceList() 29 | Expect(err).NotTo(HaveOccurred()) 30 | list, err := podFinder.GetPods(resources) 31 | Expect(err).NotTo(HaveOccurred()) 32 | Expect(list).To(HaveLen(4)) 33 | for _, v := range list { 34 | Expect(v.Items).NotTo(HaveLen(0)) 35 | } 36 | }) 37 | 38 | It("can work with an individual pod", func() { 39 | var unstructuredPod unstructured.Unstructured 40 | err := yaml.Unmarshal([]byte(GlooPodYaml), &unstructuredPod) 41 | Expect(err).NotTo(HaveOccurred()) 42 | list, err := podFinder.GetPods(kuberesource.UnstructuredResources{&unstructuredPod}) 43 | Expect(err).NotTo(HaveOccurred()) 44 | Expect(list).To(HaveLen(1)) 45 | Expect(list[0].Items).To(HaveLen(1)) 46 | pod := list[0].Items[0] 47 | Expect(pod.GetName()).To(ContainSubstring("gloo")) 48 | }) 49 | }) 50 | }) 51 | 52 | func podsAsObjects(list *corev1.PodList) []runtime.Object { 53 | result := make([]runtime.Object, len(list.Items)) 54 | for i, v := range list.Items { 55 | v := v 56 | result[i] = &v 57 | } 58 | return result 59 | } 60 | -------------------------------------------------------------------------------- /shell/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 6 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= 12 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 13 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 14 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 15 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 19 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 20 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 21 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 22 | -------------------------------------------------------------------------------- /debugutils/test/resources_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "strings" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "github.com/wx-chevalier/go-utils/debugutils" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | var _ = Describe("resource collector e2e", func() { 13 | var ( 14 | collector debugutils.ResourceCollector 15 | ) 16 | 17 | var ( 18 | containsPrefixToString = func(s string, prefixes []string) bool { 19 | for _, prefix := range prefixes { 20 | if strings.HasPrefix(s, prefix) { 21 | return true 22 | } 23 | } 24 | return false 25 | } 26 | ) 27 | 28 | Context("e2e", func() { 29 | BeforeEach(func() { 30 | var err error 31 | collector, err = debugutils.DefaultResourceCollector() 32 | Expect(err).NotTo(HaveOccurred()) 33 | }) 34 | It("can retrieve all gloo resources", func() { 35 | unstructured, err := manifests.ResourceList() 36 | Expect(err).NotTo(HaveOccurred()) 37 | collectedResources, err := collector.RetrieveResources(unstructured, "", v1.ListOptions{}) 38 | Expect(err).NotTo(HaveOccurred()) 39 | for _, resource := range collectedResources { 40 | switch resource.GVK.Kind { 41 | case "ConfigMap": 42 | Expect(resource.Resources).To(HaveLen(1)) 43 | Expect(resource.Resources[0].GetName()).To(Equal("gateway-proxy-envoy-config")) 44 | case "Pod": 45 | Expect(resource.Resources).To(HaveLen(4)) 46 | var deploymentNames []string 47 | for _, v := range unstructuredResources { 48 | if v.GetKind() == "Deployment" { 49 | deploymentNames = append(deploymentNames, v.GetName()) 50 | } 51 | } 52 | var podNames []string 53 | for _, v := range resource.Resources { 54 | podNames = append(podNames, v.GetName()) 55 | } 56 | for _, v := range podNames { 57 | Expect(containsPrefixToString(v, deploymentNames)).To(BeTrue()) 58 | } 59 | } 60 | 61 | } 62 | }) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /sliceutils/sliceutil_test.go: -------------------------------------------------------------------------------- 1 | package sliceutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wx-chevalier/go-utils/testutil" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestUniqueStringSlice(t *testing.T) { 11 | require.Equal(t, []string{}, UniqueStringSlice([]string{})) 12 | require.Equal(t, []string{"one"}, UniqueStringSlice([]string{"one"})) 13 | testutil.EqualSlicesWithoutOrder(t, 14 | []string{"one", "two"}, 15 | UniqueStringSlice([]string{"one", "two"})) 16 | testutil.EqualSlicesWithoutOrder(t, 17 | []string{"one", "two", "three"}, 18 | UniqueStringSlice([]string{"one", "two", "three", "two", "one"})) 19 | } 20 | 21 | func TestIndexOfStringInSlice(t *testing.T) { 22 | t.Log("Empty slice") 23 | require.Equal(t, -1, IndexOfStringInSlice("abc", []string{})) 24 | 25 | testSlice := []string{"abc", "def", "123", "456", "123"} 26 | 27 | t.Log("Find item") 28 | require.Equal(t, 0, IndexOfStringInSlice("abc", testSlice)) 29 | require.Equal(t, 1, IndexOfStringInSlice("def", testSlice)) 30 | require.Equal(t, 3, IndexOfStringInSlice("456", testSlice)) 31 | 32 | t.Log("Find first item, if multiple") 33 | require.Equal(t, 2, IndexOfStringInSlice("123", testSlice)) 34 | 35 | t.Log("Item is not in the slice") 36 | require.Equal(t, -1, IndexOfStringInSlice("cba", testSlice)) 37 | } 38 | 39 | func TestIsStringInSlice(t *testing.T) { 40 | t.Log("Empty slice") 41 | require.Equal(t, false, IsStringInSlice("abc", []string{})) 42 | 43 | testSlice := []string{"abc", "def", "123", "456", "123"} 44 | 45 | t.Log("Find item") 46 | require.Equal(t, true, IsStringInSlice("abc", testSlice)) 47 | require.Equal(t, true, IsStringInSlice("def", testSlice)) 48 | require.Equal(t, true, IsStringInSlice("456", testSlice)) 49 | 50 | t.Log("Find first item, if multiple") 51 | require.Equal(t, true, IsStringInSlice("123", testSlice)) 52 | 53 | t.Log("Item is not in the slice") 54 | require.Equal(t, false, IsStringInSlice("cba", testSlice)) 55 | } 56 | -------------------------------------------------------------------------------- /shell/yum.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | type Yum struct { 4 | cmd *Cmd 5 | pkg string 6 | timeout int 7 | } 8 | 9 | type yumOption func(*Yum) error 10 | 11 | func WithYumTimeout(timeout int) yumOption { 12 | return func(y *Yum) error { 13 | y.timeout = timeout 14 | return nil 15 | } 16 | } 17 | 18 | func NewYumCommand(pkg string, options ...yumOption) *Yum { 19 | yum := &Yum{ 20 | pkg: pkg, 21 | timeout: -1, 22 | } 23 | 24 | for _, opt := range options { 25 | opt(yum) 26 | } 27 | 28 | if yum.timeout > 0 { 29 | yum.cmd = NewCommand("yum install -y "+yum.pkg, WithShellMode(), WithTimeout(yum.timeout)) 30 | } else { 31 | yum.cmd = NewCommand("yum install -y "+yum.pkg, WithShellMode()) 32 | } 33 | 34 | return yum 35 | } 36 | 37 | func (y *Yum) YumInstallStart() { 38 | y.cmd.Start() 39 | } 40 | 41 | func (y *Yum) YumWait() (string, error) { 42 | y.cmd.Wait() 43 | status := y.cmd.Status 44 | 45 | if status.Error == nil && status.ExitCode == 0 { 46 | return status.Output, nil 47 | } 48 | return status.Output, status.Error 49 | } 50 | 51 | // YumInstall yum install synchorize. 52 | func YumInstall(pkg string) (string, error) { 53 | out, code, err := Command("yum -y install " + pkg) 54 | if code == 1 && err != nil { 55 | return out, err 56 | } 57 | return out, nil 58 | } 59 | 60 | // yum remove pkg. 61 | func YumRemove(pkg string) error { 62 | _, code, err := Command("yum -y remove " + pkg) 63 | if code == 0 && err == nil { 64 | return nil 65 | } 66 | return err 67 | } 68 | 69 | // YumInstallAsync Install asynchorize. 70 | // Usage: YumInstallAsync("docker", WithTimeout(1)).Then(func(res string, err error){fmt.Println(res, err)}) 71 | func YumInstallAsync(pkg string, options ...yumOption) *Yum { 72 | yumCmd := NewYumCommand(pkg, options...) 73 | yumCmd.YumInstallStart() 74 | return yumCmd 75 | } 76 | 77 | func (y *Yum) Then(f func(string, error)) { 78 | go func(y *Yum) { 79 | res, err := y.YumWait() 80 | f(res, err) 81 | }(y) 82 | } 83 | -------------------------------------------------------------------------------- /slackutils/slack.go: -------------------------------------------------------------------------------- 1 | package slackutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wx-chevalier/go-utils/contextutils" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | var _ SlackClient = new(slackClient) 11 | 12 | type SlackNotifications struct { 13 | DefaultUrl string `yaml:"default_url" json:"defaultUrl"` 14 | RepoUrls map[string]string `yaml:"repo_urls" json:"repoUrls"` 15 | } 16 | 17 | type SlackClient interface { 18 | // Use repo-specific channel, if exists 19 | NotifyForRepo(ctx context.Context, repo, message string) 20 | // Use default channel 21 | Notify(ctx context.Context, message string) 22 | } 23 | 24 | func NewSlackClient(notifications *SlackNotifications) *slackClient { 25 | return NewSlackClientForHttpClient(&DefaultHttpClient{}, notifications) 26 | } 27 | 28 | func NewSlackClientForHttpClient(httpClient HttpClient, notifications *SlackNotifications) *slackClient { 29 | return &slackClient{ 30 | httpClient: httpClient, 31 | notifications: notifications, 32 | } 33 | } 34 | 35 | type slackClient struct { 36 | httpClient HttpClient 37 | notifications *SlackNotifications 38 | } 39 | 40 | func (s *slackClient) getSlackUrl(repo string) string { 41 | if s.notifications == nil { 42 | return "" 43 | } 44 | if repo == "" || s.notifications.RepoUrls == nil { 45 | return s.notifications.DefaultUrl 46 | } 47 | repoUrl, ok := s.notifications.RepoUrls[repo] 48 | if ok { 49 | return repoUrl 50 | } 51 | return s.notifications.DefaultUrl 52 | } 53 | 54 | func (s *slackClient) Notify(ctx context.Context, message string) { 55 | s.NotifyForRepo(ctx, "", message) 56 | } 57 | 58 | func (s *slackClient) NotifyForRepo(ctx context.Context, repo, message string) { 59 | slackUrl := s.getSlackUrl(repo) 60 | if slackUrl == "" { 61 | contextutils.LoggerFrom(ctx).Warnw("Requested notifying slack, but no URL", 62 | zap.String("repo", repo), 63 | zap.Any("notifications", s.notifications)) 64 | return 65 | } 66 | s.httpClient.PostJsonContent(ctx, message, slackUrl) 67 | } 68 | -------------------------------------------------------------------------------- /template_util/templateutil.go: -------------------------------------------------------------------------------- 1 | package templateutil 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | ) 7 | 8 | // evaluateTemplate ... 9 | // 10 | // templateOptions: https://golang.org/pkg/text/template/#Template.Option 11 | func evaluateTemplate( 12 | templateContent string, 13 | inventory interface{}, 14 | funcs template.FuncMap, 15 | delimLeft, delimRight string, 16 | templateOptions []string, 17 | ) (string, error) { 18 | tmpl := template.New("").Funcs(funcs).Delims(delimLeft, delimRight) 19 | if len(templateOptions) > 0 { 20 | tmpl = tmpl.Option(templateOptions...) 21 | } 22 | tmpl, err := tmpl.Parse(templateContent) 23 | if err != nil { 24 | return "", err 25 | } 26 | 27 | var resBuffer bytes.Buffer 28 | if err := tmpl.Execute(&resBuffer, inventory); err != nil { 29 | return "", err 30 | } 31 | 32 | return resBuffer.String(), nil 33 | } 34 | 35 | // EvaluateTemplateStringToStringWithDelimiterAndOpts ... 36 | // 37 | // templateOptions: https://golang.org/pkg/text/template/#Template.Option 38 | func EvaluateTemplateStringToStringWithDelimiterAndOpts( 39 | templateContent string, 40 | inventory interface{}, 41 | funcs template.FuncMap, 42 | delimLeft, delimRight string, 43 | templateOptions []string, 44 | ) (string, error) { 45 | return evaluateTemplate(templateContent, inventory, funcs, delimLeft, delimRight, templateOptions) 46 | } 47 | 48 | // EvaluateTemplateStringToStringWithDelimiter ... 49 | func EvaluateTemplateStringToStringWithDelimiter( 50 | templateContent string, 51 | inventory interface{}, 52 | funcs template.FuncMap, 53 | delimLeft, delimRight string, 54 | ) (string, error) { 55 | return evaluateTemplate(templateContent, inventory, funcs, delimLeft, delimRight, []string{}) 56 | } 57 | 58 | // EvaluateTemplateStringToString ... 59 | func EvaluateTemplateStringToString(templateContent string, inventory interface{}, funcs template.FuncMap) (string, error) { 60 | return EvaluateTemplateStringToStringWithDelimiter(templateContent, inventory, funcs, "", "") 61 | } 62 | -------------------------------------------------------------------------------- /pkcs12utils/internal/rc2/rc2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package rc2 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "testing" 11 | ) 12 | 13 | func TestEncryptDecrypt(t *testing.T) { 14 | // TODO(dgryski): add the rest of the test vectors from the RFC 15 | var tests = []struct { 16 | key string 17 | plain string 18 | cipher string 19 | t1 int 20 | }{ 21 | { 22 | "0000000000000000", 23 | "0000000000000000", 24 | "ebb773f993278eff", 25 | 63, 26 | }, 27 | { 28 | "ffffffffffffffff", 29 | "ffffffffffffffff", 30 | "278b27e42e2f0d49", 31 | 64, 32 | }, 33 | { 34 | "3000000000000000", 35 | "1000000000000001", 36 | "30649edf9be7d2c2", 37 | 64, 38 | }, 39 | { 40 | "88", 41 | "0000000000000000", 42 | "61a8a244adacccf0", 43 | 64, 44 | }, 45 | { 46 | "88bca90e90875a", 47 | "0000000000000000", 48 | "6ccf4308974c267f", 49 | 64, 50 | }, 51 | { 52 | "88bca90e90875a7f0f79c384627bafb2", 53 | "0000000000000000", 54 | "1a807d272bbe5db1", 55 | 64, 56 | }, 57 | { 58 | "88bca90e90875a7f0f79c384627bafb2", 59 | "0000000000000000", 60 | "2269552ab0f85ca6", 61 | 128, 62 | }, 63 | { 64 | "88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e", 65 | "0000000000000000", 66 | "5b78d3a43dfff1f1", 67 | 129, 68 | }, 69 | } 70 | 71 | for _, tt := range tests { 72 | k, _ := hex.DecodeString(tt.key) 73 | p, _ := hex.DecodeString(tt.plain) 74 | c, _ := hex.DecodeString(tt.cipher) 75 | 76 | b, _ := New(k, tt.t1) 77 | 78 | var dst [8]byte 79 | 80 | b.Encrypt(dst[:], p) 81 | 82 | if !bytes.Equal(dst[:], c) { 83 | t.Errorf("encrypt failed: got % 2x wanted % 2x\n", dst, c) 84 | } 85 | 86 | b.Decrypt(dst[:], c) 87 | 88 | if !bytes.Equal(dst[:], p) { 89 | t.Errorf("decrypt failed: got % 2x wanted % 2x\n", dst, p) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /progressutils/spinner.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "time" 8 | "unicode/utf8" 9 | ) 10 | 11 | // Spinner ... 12 | type Spinner struct { 13 | message string 14 | chars []string 15 | delay time.Duration 16 | writer io.Writer 17 | 18 | active bool 19 | lastOutput string 20 | stopChan chan bool 21 | } 22 | 23 | // NewSpinner ... 24 | func NewSpinner(message string, chars []string, delay time.Duration, writer io.Writer) Spinner { 25 | return Spinner{ 26 | message: message, 27 | chars: chars, 28 | delay: delay, 29 | writer: writer, 30 | 31 | active: false, 32 | stopChan: make(chan bool), 33 | } 34 | } 35 | 36 | // NewDefaultSpinner ... 37 | func NewDefaultSpinner(message string) Spinner { 38 | chars := []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"} 39 | delay := 100 * time.Millisecond 40 | writer := os.Stdout 41 | return NewSpinner(message, chars, delay, writer) 42 | } 43 | 44 | func (s *Spinner) erase() { 45 | n := utf8.RuneCountInString(s.lastOutput) 46 | for _, c := range []string{"\b", " ", "\b"} { 47 | for i := 0; i < n; i++ { 48 | if _, err := fmt.Fprintf(s.writer, c); err != nil { 49 | fmt.Printf("failed to update progress, error: %s\n", err) 50 | } 51 | } 52 | } 53 | s.lastOutput = "" 54 | } 55 | 56 | // Start ... 57 | func (s *Spinner) Start() { 58 | if s.active { 59 | return 60 | } 61 | s.active = true 62 | 63 | go func() { 64 | for { 65 | for i := 0; i < len(s.chars); i++ { 66 | select { 67 | case <-s.stopChan: 68 | return 69 | default: 70 | s.erase() 71 | 72 | out := fmt.Sprintf("%s %s", s.message, s.chars[i]) 73 | if _, err := fmt.Fprint(s.writer, out); err != nil { 74 | fmt.Printf("failed to update progress, error: %s\n", err) 75 | } 76 | s.lastOutput = out 77 | 78 | time.Sleep(s.delay) 79 | } 80 | } 81 | } 82 | }() 83 | } 84 | 85 | // Stop ... 86 | func (s *Spinner) Stop() { 87 | if s.active { 88 | s.active = false 89 | s.erase() 90 | s.stopChan <- true 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /testutils/helper/testrunner.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/wx-chevalier/go-utils/log" 8 | ) 9 | 10 | const ( 11 | defaultTestRunnerImage = "soloio/testrunner:latest" 12 | TestrunnerName = "testrunner" 13 | TestRunnerPort = 1234 14 | 15 | // This response is given by the testrunner when the SimpleServer is started 16 | SimpleHttpResponse = ` 17 | Directory listing for / 18 | 19 |

Directory listing for /

20 |
21 | 28 |
29 | 30 | ` 31 | ) 32 | 33 | func NewTestRunner(namespace string) (*testRunner, error) { 34 | testContainer, err := newTestContainer(namespace, defaultTestRunnerImage, TestrunnerName, TestRunnerPort) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return &testRunner{ 40 | testContainer: testContainer, 41 | }, nil 42 | } 43 | 44 | // This object represents a container that gets deployed to the cluster to support testing. 45 | type testRunner struct { 46 | *testContainer 47 | } 48 | 49 | func (t *testRunner) Deploy(timeout time.Duration) error { 50 | err := t.deploy(timeout) 51 | if err != nil { 52 | return err 53 | } 54 | go func() { 55 | start := time.Now() 56 | log.Debugf("starting http server listening on port %v", TestRunnerPort) 57 | // This command start an http SimpleHttpServer and blocks until the server terminates 58 | if _, err := t.Exec("python", "-m", "SimpleHTTPServer", fmt.Sprintf("%v", TestRunnerPort)); err != nil { 59 | // if an error happened after 5 seconds, it's probably not an error.. just the pod terminating. 60 | if time.Now().Sub(start).Seconds() < 5.0 { 61 | log.Warnf("failed to start HTTP Server in Test Runner: %v", err) 62 | } 63 | } 64 | }() 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /progressutils/progress_test.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestSimpleProgress(t *testing.T) { 12 | startTime := time.Now() 13 | 14 | SimpleProgress(".", 500*time.Millisecond, func() { 15 | t.Log("- SimpleProgress [start] -") 16 | time.Sleep(3 * time.Second) 17 | t.Log("- SimpleProgress [end] -") 18 | }) 19 | 20 | duration := time.Now().Sub(startTime) 21 | if duration >= time.Duration(4)*time.Second { 22 | t.Fatalf("Should take no more than 4 sec, but got: %s", duration) 23 | } 24 | if duration < time.Duration(2)*time.Second { 25 | t.Fatalf("Should take at least 2 sec, but got: %s", duration) 26 | } 27 | } 28 | 29 | func TestSimpleProgressE(t *testing.T) { 30 | t.Log("No error") 31 | { 32 | startTime := time.Now() 33 | actionErr := SimpleProgressE(".", 500*time.Millisecond, func() error { 34 | t.Log("- SimpleProgressE [start] -") 35 | time.Sleep(3 * time.Second) 36 | t.Log("- SimpleProgressE [end] -") 37 | return nil 38 | }) 39 | require.NoError(t, actionErr) 40 | 41 | duration := time.Now().Sub(startTime) 42 | if duration >= time.Duration(4)*time.Second { 43 | t.Fatalf("Should take no more than 4 sec, but got: %s", duration) 44 | } 45 | if duration < time.Duration(2)*time.Second { 46 | t.Fatalf("Should take at least 2 sec, but got: %s", duration) 47 | } 48 | } 49 | 50 | t.Log("Return error") 51 | { 52 | startTime := time.Now() 53 | actionErr := SimpleProgressE(".", 500*time.Millisecond, func() error { 54 | t.Log("- SimpleProgressE [start] -") 55 | time.Sleep(3 * time.Second) 56 | t.Log("- SimpleProgressE [end] -") 57 | return errors.New("Test error") 58 | }) 59 | require.EqualError(t, actionErr, "Test error") 60 | 61 | duration := time.Now().Sub(startTime) 62 | if duration >= time.Duration(4)*time.Second { 63 | t.Fatalf("Should take no more than 4 sec, but got: %s", duration) 64 | } 65 | if duration < time.Duration(2)*time.Second { 66 | t.Fatalf("Should take at least 2 sec, but got: %s", duration) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /botutils/botconfig/os_mock_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/wx-chevalier/go-utils/osutils (interfaces: OsClient) 3 | 4 | // Package botconfig_test is a generated GoMock package. 5 | package botconfig_test 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | gomock "github.com/golang/mock/gomock" 11 | ) 12 | 13 | // MockOsClient is a mock of OsClient interface 14 | type MockOsClient struct { 15 | ctrl *gomock.Controller 16 | recorder *MockOsClientMockRecorder 17 | } 18 | 19 | // MockOsClientMockRecorder is the mock recorder for MockOsClient 20 | type MockOsClientMockRecorder struct { 21 | mock *MockOsClient 22 | } 23 | 24 | // NewMockOsClient creates a new mock instance 25 | func NewMockOsClient(ctrl *gomock.Controller) *MockOsClient { 26 | mock := &MockOsClient{ctrl: ctrl} 27 | mock.recorder = &MockOsClientMockRecorder{mock} 28 | return mock 29 | } 30 | 31 | // EXPECT returns an object that allows the caller to indicate expected use 32 | func (m *MockOsClient) EXPECT() *MockOsClientMockRecorder { 33 | return m.recorder 34 | } 35 | 36 | // Getenv mocks base method 37 | func (m *MockOsClient) Getenv(arg0 string) string { 38 | m.ctrl.T.Helper() 39 | ret := m.ctrl.Call(m, "Getenv", arg0) 40 | ret0, _ := ret[0].(string) 41 | return ret0 42 | } 43 | 44 | // Getenv indicates an expected call of Getenv 45 | func (mr *MockOsClientMockRecorder) Getenv(arg0 interface{}) *gomock.Call { 46 | mr.mock.ctrl.T.Helper() 47 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Getenv", reflect.TypeOf((*MockOsClient)(nil).Getenv), arg0) 48 | } 49 | 50 | // ReadFile mocks base method 51 | func (m *MockOsClient) ReadFile(arg0 string) ([]byte, error) { 52 | m.ctrl.T.Helper() 53 | ret := m.ctrl.Call(m, "ReadFile", arg0) 54 | ret0, _ := ret[0].([]byte) 55 | ret1, _ := ret[1].(error) 56 | return ret0, ret1 57 | } 58 | 59 | // ReadFile indicates an expected call of ReadFile 60 | func (mr *MockOsClientMockRecorder) ReadFile(arg0 interface{}) *gomock.Call { 61 | mr.mock.ctrl.T.Helper() 62 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFile", reflect.TypeOf((*MockOsClient)(nil).ReadFile), arg0) 63 | } 64 | -------------------------------------------------------------------------------- /pointersutils/pointers.go: -------------------------------------------------------------------------------- 1 | package pointers 2 | 3 | import "time" 4 | 5 | // NewBoolPtr ... 6 | func NewBoolPtr(val bool) *bool { 7 | ptrValue := new(bool) 8 | *ptrValue = val 9 | return ptrValue 10 | } 11 | 12 | // NewStringPtr ... 13 | func NewStringPtr(val string) *string { 14 | ptrValue := new(string) 15 | *ptrValue = val 16 | return ptrValue 17 | } 18 | 19 | // NewTimePtr ... 20 | func NewTimePtr(val time.Time) *time.Time { 21 | ptrValue := new(time.Time) 22 | *ptrValue = val 23 | return ptrValue 24 | } 25 | 26 | // NewIntPtr ... 27 | func NewIntPtr(val int) *int { 28 | ptrValue := new(int) 29 | *ptrValue = val 30 | return ptrValue 31 | } 32 | 33 | // NewInt64Ptr ... 34 | func NewInt64Ptr(val int64) *int64 { 35 | ptrValue := new(int64) 36 | *ptrValue = val 37 | return ptrValue 38 | } 39 | 40 | // NewMapStringInterfacePtr ... 41 | func NewMapStringInterfacePtr(val map[string]interface{}) *map[string]interface{} { 42 | ptrValue := new(map[string]interface{}) 43 | *ptrValue = map[string]interface{}{} 44 | for key, value := range val { 45 | (*ptrValue)[key] = value 46 | } 47 | return ptrValue 48 | } 49 | 50 | // ------------------------------------------------------ 51 | // --- Safe Getters 52 | 53 | // Bool ... 54 | func Bool(val *bool) bool { 55 | return BoolWithDefault(val, false) 56 | } 57 | 58 | // BoolWithDefault ... 59 | func BoolWithDefault(val *bool, defaultValue bool) bool { 60 | if val == nil { 61 | return defaultValue 62 | } 63 | return *val 64 | } 65 | 66 | // String ... 67 | func String(val *string) string { 68 | return StringWithDefault(val, "") 69 | } 70 | 71 | // StringWithDefault ... 72 | func StringWithDefault(val *string, defaultValue string) string { 73 | if val == nil { 74 | return defaultValue 75 | } 76 | return *val 77 | } 78 | 79 | // TimeWithDefault ... 80 | func TimeWithDefault(val *time.Time, defaultValue time.Time) time.Time { 81 | if val == nil { 82 | return defaultValue 83 | } 84 | return *val 85 | } 86 | 87 | // Int ... 88 | func Int(val *int) int { 89 | return IntWithDefault(val, 0) 90 | } 91 | 92 | // IntWithDefault ... 93 | func IntWithDefault(val *int, defaultValue int) int { 94 | if val == nil { 95 | return defaultValue 96 | } 97 | return *val 98 | } 99 | -------------------------------------------------------------------------------- /debugutils/storage.go: -------------------------------------------------------------------------------- 1 | package debugutils 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | 9 | "cloud.google.com/go/storage" 10 | "github.com/spf13/afero" 11 | "golang.org/x/sync/errgroup" 12 | ) 13 | 14 | type StorageObject struct { 15 | Resource io.Reader 16 | Name string 17 | } 18 | 19 | type StorageClient interface { 20 | Save(location string, resources ...*StorageObject) error 21 | } 22 | 23 | type FileStorageClient struct { 24 | fs afero.Fs 25 | } 26 | 27 | func NewFileStorageClient(fs afero.Fs) *FileStorageClient { 28 | return &FileStorageClient{fs: fs} 29 | } 30 | 31 | func DefaultFileStorageClient() *FileStorageClient { 32 | return &FileStorageClient{fs: afero.NewOsFs()} 33 | } 34 | 35 | func (fsc *FileStorageClient) Save(location string, resources ...*StorageObject) error { 36 | for _, resource := range resources { 37 | fileName := filepath.Join(location, resource.Name) 38 | file, err := fsc.fs.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0777) 39 | if err != nil { 40 | return err 41 | } 42 | _, err = io.Copy(file, resource.Resource) 43 | if err != nil { 44 | return err 45 | } 46 | file.Close() 47 | } 48 | return nil 49 | } 50 | 51 | type GcsStorageClient struct { 52 | client *storage.Client 53 | ctx context.Context 54 | } 55 | 56 | func NewGcsStorageClient(client *storage.Client, ctx context.Context) *GcsStorageClient { 57 | return &GcsStorageClient{client: client, ctx: ctx} 58 | } 59 | 60 | func DefaultGcsStorageClient(ctx context.Context) (*GcsStorageClient, error) { 61 | client, err := storage.NewClient(ctx) 62 | if err != nil { 63 | return nil, err 64 | } 65 | return &GcsStorageClient{client: client, ctx: ctx}, nil 66 | } 67 | 68 | func (gsc *GcsStorageClient) Save(location string, resources ...*StorageObject) error { 69 | bucket := gsc.client.Bucket(location) 70 | eg := errgroup.Group{} 71 | for _, resource := range resources { 72 | resource := resource 73 | eg.Go(func() error { 74 | obj := bucket.Object(resource.Name) 75 | w := obj.NewWriter(gsc.ctx) 76 | defer w.Close() 77 | _, err := io.Copy(w, resource.Resource) 78 | if err != nil { 79 | return err 80 | } 81 | return nil 82 | }) 83 | } 84 | return eg.Wait() 85 | } 86 | -------------------------------------------------------------------------------- /logutils/internal_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "fmt" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | var ( 13 | analyticsServerURL = "https://bitrise-step-analytics.herokuapp.com" 14 | httpClient = http.Client{ 15 | Timeout: time.Second * 5, 16 | } 17 | ) 18 | 19 | // Entry represents a line in a log 20 | type Entry struct { 21 | LogLevel string `json:"log_level"` 22 | Message string `json:"message"` 23 | Data map[string]interface{} `json:"data"` 24 | } 25 | 26 | // SetAnalyticsServerURL updates the the analytics server collecting the 27 | // logs. It is intended for use during tests. Warning: current implementation 28 | // is not thread safe, do not call the function during runtime. 29 | func SetAnalyticsServerURL(url string) { 30 | analyticsServerURL = url 31 | } 32 | 33 | // Internal sends the log message to the configured analytics server 34 | func rprintf(logLevel string, stepID string, tag string, data map[string]interface{}, format string, v ...interface{}) { 35 | e := Entry{ 36 | Message: fmt.Sprintf(format, v...), 37 | LogLevel: logLevel, 38 | } 39 | 40 | e.Data = make(map[string]interface{}) 41 | for k, v := range data { 42 | e.Data[k] = v 43 | } 44 | 45 | if v, ok := e.Data["step_id"]; ok { 46 | fmt.Printf("internal logger: data.step_id (%s) will be overriden with (%s) ", v, stepID) 47 | } 48 | if v, ok := e.Data["tag"]; ok { 49 | fmt.Printf("internal logger: data.tag (%s) will be overriden with (%s) ", v, tag) 50 | } 51 | 52 | e.Data["step_id"] = stepID 53 | e.Data["tag"] = tag 54 | 55 | var b bytes.Buffer 56 | if err := json.NewEncoder(&b).Encode(e); err != nil { 57 | return 58 | } 59 | 60 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 61 | defer cancel() 62 | 63 | req, err := http.NewRequest(http.MethodPost, analyticsServerURL+"/logs", &b) 64 | if err != nil { 65 | // deliberately not writing into users log 66 | return 67 | } 68 | req = req.WithContext(ctx) 69 | req.Header.Add("Content-Type", "application/json") 70 | 71 | if _, err := httpClient.Do(req); err != nil { 72 | // deliberately not writing into users log 73 | return 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /debugutils/mocks_kube_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: k8s.io/client-go/rest (interfaces: ResponseWrapper) 3 | 4 | // Package debugutils is a generated GoMock package. 5 | package debugutils 6 | 7 | import ( 8 | io "io" 9 | reflect "reflect" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | ) 13 | 14 | // MockResponseWrapper is a mock of ResponseWrapper interface 15 | type MockResponseWrapper struct { 16 | ctrl *gomock.Controller 17 | recorder *MockResponseWrapperMockRecorder 18 | } 19 | 20 | // MockResponseWrapperMockRecorder is the mock recorder for MockResponseWrapper 21 | type MockResponseWrapperMockRecorder struct { 22 | mock *MockResponseWrapper 23 | } 24 | 25 | // NewMockResponseWrapper creates a new mock instance 26 | func NewMockResponseWrapper(ctrl *gomock.Controller) *MockResponseWrapper { 27 | mock := &MockResponseWrapper{ctrl: ctrl} 28 | mock.recorder = &MockResponseWrapperMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use 33 | func (m *MockResponseWrapper) EXPECT() *MockResponseWrapperMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // DoRaw mocks base method 38 | func (m *MockResponseWrapper) DoRaw() ([]byte, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "DoRaw") 41 | ret0, _ := ret[0].([]byte) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // DoRaw indicates an expected call of DoRaw 47 | func (mr *MockResponseWrapperMockRecorder) DoRaw() *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DoRaw", reflect.TypeOf((*MockResponseWrapper)(nil).DoRaw)) 50 | } 51 | 52 | // Stream mocks base method 53 | func (m *MockResponseWrapper) Stream() (io.ReadCloser, error) { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "Stream") 56 | ret0, _ := ret[0].(io.ReadCloser) 57 | ret1, _ := ret[1].(error) 58 | return ret0, ret1 59 | } 60 | 61 | // Stream indicates an expected call of Stream 62 | func (mr *MockResponseWrapperMockRecorder) Stream() *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stream", reflect.TypeOf((*MockResponseWrapper)(nil).Stream)) 65 | } 66 | -------------------------------------------------------------------------------- /testutils/kube/kube_create.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | . "github.com/onsi/gomega" 8 | "github.com/wx-chevalier/go-utils/contextutils" 9 | "github.com/wx-chevalier/go-utils/errutils" 10 | "github.com/wx-chevalier/go-utils/kubeutils" 11 | "go.uber.org/zap" 12 | kubev1 "k8s.io/api/core/v1" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | "k8s.io/client-go/kubernetes" 15 | ) 16 | 17 | func CreateNs(ns string) error { 18 | kube := MustKubeClient() 19 | _, err := kube.CoreV1().Namespaces().Create(&kubev1.Namespace{ 20 | ObjectMeta: metav1.ObjectMeta{ 21 | Name: ns, 22 | }, 23 | }) 24 | 25 | return err 26 | } 27 | 28 | func MustCreateNs(ns string) { 29 | ExpectWithOffset(1, CreateNs(ns)).NotTo(HaveOccurred()) 30 | } 31 | 32 | func DeleteNs(ns string) error { 33 | kube := MustKubeClient() 34 | err := kube.CoreV1().Namespaces().Delete(ns, nil) 35 | 36 | return err 37 | } 38 | 39 | func MustDeleteNs(ns string) { 40 | ExpectWithOffset(1, DeleteNs(ns)).NotTo(HaveOccurred()) 41 | } 42 | 43 | func ConfigMap(ns, name, data string, labels map[string]string) kubev1.ConfigMap { 44 | return kubev1.ConfigMap{ 45 | TypeMeta: metav1.TypeMeta{ 46 | APIVersion: "v1", 47 | Kind: "ConfigMap", 48 | }, 49 | ObjectMeta: metav1.ObjectMeta{ 50 | Name: name, 51 | Namespace: ns, 52 | Labels: labels, 53 | }, 54 | Data: map[string]string{"data": data}, 55 | } 56 | } 57 | 58 | func CreateConfigMap(cm kubev1.ConfigMap) error { 59 | kube := MustKubeClient() 60 | _, err := kube.CoreV1().ConfigMaps(cm.Namespace).Create(&cm) 61 | 62 | return err 63 | } 64 | 65 | func MustCreateConfigMap(cm kubev1.ConfigMap) { 66 | ExpectWithOffset(1, CreateConfigMap(cm)).NotTo(HaveOccurred()) 67 | } 68 | 69 | func MustKubeClient() kubernetes.Interface { 70 | client, err := KubeClient() 71 | if err != nil { 72 | contextutils.LoggerFrom(context.TODO()).Fatalw("failed to create kube client", zap.Error(err)) 73 | } 74 | return client 75 | } 76 | 77 | func KubeClient() (kubernetes.Interface, error) { 78 | cfg, err := kubeutils.GetConfig("", os.Getenv("KUBECONFIG")) 79 | if err != nil { 80 | return nil, errors.Wrapf(err, "getting kube config") 81 | } 82 | return kubernetes.NewForConfig(cfg) 83 | } 84 | -------------------------------------------------------------------------------- /kubeinstallutils/helmchart/docs_test.go: -------------------------------------------------------------------------------- 1 | package helmchart_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | . "github.com/wx-chevalier/go-utils/installutils/helmchart" 8 | ) 9 | 10 | type Config struct { 11 | Namespace *Namespace `json:"namespace,omitempty" desc:"create namespace"` 12 | Array []Array `json:"array,omitempty"` 13 | Bool bool `json:"booleanValue,omitempty"` 14 | Complex Complex `json:"complex,omitempty"` 15 | } 16 | 17 | type Complex struct { 18 | SomeMap map[string]string `json:"items"` 19 | } 20 | 21 | type Namespace struct { 22 | Create bool `json:"create" desc:"create the installation namespace"` 23 | } 24 | 25 | type Array struct { 26 | Something string `json:"something" desc:"create something"` 27 | } 28 | 29 | var _ = Describe("Docs", func() { 30 | It("should document helm values", func() { 31 | c := Config{ 32 | Namespace: &Namespace{Create: true}, 33 | Bool: true, 34 | Complex: Complex{SomeMap: map[string]string{"default": "yes"}}, 35 | } 36 | docDesc := Doc(c) 37 | 38 | expectedDocs := HelmValues{ 39 | { 40 | Key: "namespace.create", 41 | Type: "bool", 42 | DefaultValue: "true", 43 | Description: "create the installation namespace", 44 | }, 45 | { 46 | Key: "array[].something", 47 | Type: "string", 48 | DefaultValue: "", 49 | Description: "create something", 50 | }, 51 | {Key: "booleanValue", Type: "bool", DefaultValue: "true", Description: ""}, 52 | { 53 | Key: "complex.items.NAME", 54 | Type: "string", 55 | DefaultValue: "", 56 | Description: "", 57 | }, 58 | { 59 | Key: "complex.items.default", 60 | Type: "string", 61 | DefaultValue: "yes", 62 | Description: "", 63 | }, 64 | } 65 | 66 | Expect(expectedDocs).To(Equal(docDesc)) 67 | }) 68 | 69 | It("should print markdown", func() { 70 | values := HelmValues{{Key: "key", Type: "type", DefaultValue: "default", Description: "desc"}} 71 | expected := "|Option|Type|Default Value|Description|\n|------|----|-----------|-------------|\n|key|type|default|desc|\n" 72 | 73 | Expect(expected).To(Equal(values.ToMarkdown())) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /kubeinstallutils/kuberesource/get_cluster_resources_test.go: -------------------------------------------------------------------------------- 1 | package kuberesource 2 | 3 | import ( 4 | "context" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "github.com/wx-chevalier/go-utils/kubeutils" 9 | "github.com/wx-chevalier/go-utils/testutils" 10 | utils "github.com/wx-chevalier/go-utils/testutils/kube" 11 | v1 "k8s.io/api/core/v1" 12 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 13 | "k8s.io/apimachinery/pkg/runtime/schema" 14 | ) 15 | 16 | var _ = Describe("GetClusterResources", func() { 17 | var ns string 18 | labelSetA := map[string]string{"a": "b"} 19 | var cm1, cm2, cm3 v1.ConfigMap 20 | BeforeEach(func() { 21 | ns = "test" + testutils.RandString(4) 22 | cm1, cm2, cm3 = utils.ConfigMap(ns, "a1", "data1", labelSetA), 23 | utils.ConfigMap(ns, "a2", "data2", labelSetA), 24 | utils.ConfigMap(ns, "a3", "data3", labelSetA) 25 | utils.MustCreateNs(ns) 26 | utils.MustCreateConfigMap(cm1) 27 | utils.MustCreateConfigMap(cm2) 28 | utils.MustCreateConfigMap(cm3) 29 | }) 30 | AfterEach(func() { 31 | utils.MustDeleteNs(ns) 32 | }) 33 | It("gets all resources in the cluster, period", func() { 34 | cfg, err := kubeutils.GetConfig("", "") 35 | Expect(err).NotTo(HaveOccurred()) 36 | allRes, err := GetClusterResources(context.TODO(), cfg, func(resource schema.GroupVersionResource) bool { 37 | // just get configmaps 38 | if resource.Resource != "configmaps" { 39 | return true 40 | } 41 | return false 42 | }) 43 | Expect(err).NotTo(HaveOccurred()) 44 | cmInOurNs := allRes.Filter(func(resource *unstructured.Unstructured) bool { 45 | return resource.GetNamespace() != ns 46 | }) 47 | 48 | expected1, err := ConvertToUnstructured(&cm1) 49 | Expect(err).NotTo(HaveOccurred()) 50 | expected2, err := ConvertToUnstructured(&cm2) 51 | Expect(err).NotTo(HaveOccurred()) 52 | expected3, err := ConvertToUnstructured(&cm3) 53 | Expect(err).NotTo(HaveOccurred()) 54 | expected := UnstructuredResources{expected1, expected2, expected3} 55 | 56 | Expect(cmInOurNs).To(HaveLen(3)) 57 | for i := range expected { 58 | actual := cmInOurNs[i] 59 | delete(actual.Object, "metadata") 60 | delete(expected[i].Object, "metadata") 61 | Expect(actual).To(Equal(expected[i])) 62 | } 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /strutils/regex_test.go: -------------------------------------------------------------------------------- 1 | package regexputil 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestNamedFindStringSubmatch(t *testing.T) { 11 | t.Log("Both the name and age group are required") 12 | rexp := regexp.MustCompile(`(?P[a-zA-Z]+) (?P[0-9]+)`) 13 | 14 | t.Log("Simple name+age example") 15 | { 16 | results, isFound := NamedFindStringSubmatch(rexp, "MyName 42") 17 | require.Equal(t, true, isFound) 18 | require.Equal(t, map[string]string{ 19 | "name": "MyName", 20 | "age": "42", 21 | }, results) 22 | } 23 | 24 | t.Log("Includes an additional name at the end") 25 | { 26 | results, isFound := NamedFindStringSubmatch(rexp, "MyName 42 AnotherName") 27 | require.Equal(t, true, isFound) 28 | require.Equal(t, map[string]string{ 29 | "name": "MyName", 30 | "age": "42", 31 | }, results) 32 | } 33 | 34 | t.Log("Includes an additional name at the start") 35 | { 36 | results, isFound := NamedFindStringSubmatch(rexp, "AnotherName MyName 42") 37 | require.Equal(t, true, isFound) 38 | require.Equal(t, map[string]string{ 39 | "name": "MyName", 40 | "age": "42", 41 | }, results) 42 | } 43 | 44 | t.Log("Missing name group - should error") 45 | { 46 | results, isFound := NamedFindStringSubmatch(rexp, " 42") 47 | require.Equal(t, false, isFound) 48 | require.Equal(t, map[string]string(nil), results) 49 | } 50 | 51 | t.Log("Missing age group - should error") 52 | { 53 | results, isFound := NamedFindStringSubmatch(rexp, "MyName ") 54 | require.Equal(t, false, isFound) 55 | require.Equal(t, map[string]string(nil), results) 56 | } 57 | 58 | t.Log("Missing both groups - should error") 59 | { 60 | results, isFound := NamedFindStringSubmatch(rexp, "") 61 | require.Equal(t, false, isFound) 62 | require.Equal(t, map[string]string(nil), results) 63 | } 64 | 65 | t.Log("Optional name part") 66 | rexp = regexp.MustCompile(`(?P[a-zA-Z]*) (?P[0-9]+)`) 67 | 68 | t.Log("Name can now be empty - but should be included in the result!") 69 | { 70 | results, isFound := NamedFindStringSubmatch(rexp, " 42") 71 | require.Equal(t, true, isFound) 72 | require.Equal(t, map[string]string{ 73 | "name": "", 74 | "age": "42", 75 | }, results) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /testutils/kube/istio_teardown.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | . "github.com/onsi/gomega" 8 | "github.com/wx-chevalier/go-utils/kubeutils" 9 | kubev1 "k8s.io/api/core/v1" 10 | apiexts "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/client-go/kubernetes" 13 | 14 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | ) 16 | 17 | func WaitForServicesInNamespaceTeardown(ns string) { 18 | EventuallyWithOffset(1, func() []kubev1.Service { 19 | svcs, err := MustKubeClient().CoreV1().Services(ns).List(v1.ListOptions{}) 20 | if err != nil { 21 | // namespace is gone 22 | return []kubev1.Service{} 23 | } 24 | return svcs.Items 25 | }, time.Second*30).Should(BeEmpty()) 26 | } 27 | 28 | func TeardownClusterResourcesWithPrefix(kube kubernetes.Interface, prefix string) { 29 | clusterroles, err := kube.RbacV1beta1().ClusterRoles().List(metav1.ListOptions{}) 30 | if err == nil { 31 | for _, cr := range clusterroles.Items { 32 | if strings.Contains(cr.Name, prefix) { 33 | kube.RbacV1beta1().ClusterRoles().Delete(cr.Name, nil) 34 | } 35 | } 36 | } 37 | clusterrolebindings, err := kube.RbacV1beta1().ClusterRoleBindings().List(metav1.ListOptions{}) 38 | if err == nil { 39 | for _, cr := range clusterrolebindings.Items { 40 | if strings.Contains(cr.Name, prefix) { 41 | kube.RbacV1beta1().ClusterRoleBindings().Delete(cr.Name, nil) 42 | } 43 | } 44 | } 45 | webhooks, err := kube.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().List(metav1.ListOptions{}) 46 | if err == nil { 47 | for _, wh := range webhooks.Items { 48 | if strings.Contains(wh.Name, prefix) { 49 | kube.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(wh.Name, nil) 50 | } 51 | } 52 | } 53 | 54 | cfg, err := kubeutils.GetConfig("", "") 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | exts, err := apiexts.NewForConfig(cfg) 58 | Expect(err).NotTo(HaveOccurred()) 59 | 60 | crds, err := exts.ApiextensionsV1beta1().CustomResourceDefinitions().List(metav1.ListOptions{}) 61 | if err == nil { 62 | for _, cr := range crds.Items { 63 | if strings.Contains(cr.Name, prefix) { 64 | exts.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(cr.Name, nil) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vfsutils/afero.go: -------------------------------------------------------------------------------- 1 | package vfsutils 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "path/filepath" 7 | 8 | "github.com/google/go-github/github" 9 | "github.com/wx-chevalier/go-utils/githubutils" 10 | "github.com/wx-chevalier/go-utils/tarutils" 11 | 12 | "github.com/spf13/afero" 13 | ) 14 | 15 | func MountCode(fs afero.Fs, ctx context.Context, client *github.Client, owner, repo, ref string) (dir string, err error) { 16 | tarFile, codeDir, err := setupTemporaryFiles(fs) 17 | if err != nil { 18 | return "", err 19 | } 20 | defer fs.Remove(tarFile.Name()) 21 | if err := githubutils.DownloadRepoArchive(ctx, client, tarFile, owner, repo, ref); err != nil { 22 | return "", err 23 | } 24 | if err := tarutils.Untar(codeDir, tarFile.Name(), fs); err != nil { 25 | return "", err 26 | } 27 | repoFolderName, err := getRepoFolder(fs, codeDir) 28 | if err != nil { 29 | return "", err 30 | } 31 | return repoFolderName, nil 32 | } 33 | 34 | func MountTar(fs afero.Fs, tarUrl string) (dir string, err error) { 35 | tarFile, untarDir, err := setupTemporaryFiles(fs) 36 | if err != nil { 37 | return "", err 38 | } 39 | defer fs.Remove(tarFile.Name()) 40 | 41 | if err := githubutils.DownloadFile(tarUrl, tarFile); err != nil { 42 | return "", err 43 | } 44 | if err := tarutils.Untar(untarDir, tarFile.Name(), fs); err != nil { 45 | return "", err 46 | } 47 | return untarDir, nil 48 | } 49 | 50 | func setupTemporaryFiles(fs afero.Fs) (file afero.File, dir string, err error) { 51 | tmpf, err := afero.TempFile(fs, "", "tar-file-") 52 | if err != nil { 53 | return nil, "", err 54 | } 55 | 56 | tmpd, err := afero.TempDir(fs, "", "tar-dir-") 57 | if err != nil { 58 | return nil, "", err 59 | } 60 | return tmpf, tmpd, err 61 | } 62 | 63 | func getRepoFolder(fs afero.Fs, tmpd string) (string, error) { 64 | files, err := afero.ReadDir(fs, tmpd) 65 | if err != nil { 66 | return "", err 67 | } 68 | if len(files) != 1 { 69 | return "", fmt.Errorf("expected only one folder from archive tar, found (%d)", len(files)) 70 | } 71 | 72 | var repoDirName string 73 | for _, file := range files { 74 | if file.IsDir() { 75 | repoDirName = file.Name() 76 | } 77 | } 78 | if repoDirName == "" { 79 | return "", fmt.Errorf("unable to find directory in archive of git repo") 80 | } 81 | return filepath.Join(tmpd, repoDirName), nil 82 | } 83 | -------------------------------------------------------------------------------- /grpcx/balancer.go: -------------------------------------------------------------------------------- 1 | package grpcx 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/naming" 9 | ) 10 | 11 | var ErrWatcherClose = errors.New("watcher has been closed") 12 | 13 | func NewDnsBalancer() grpc.Balancer { 14 | r, _ := naming.NewDNSResolver() 15 | return grpc.RoundRobin(r) 16 | } 17 | 18 | // Example 19 | // conn, err := grpc.Dial( 20 | // "shark", 21 | // grpc.WithInsecure(), 22 | // grpc.WithBalancer(grpc.RoundRobin((NewHelpResolver.serverAddrs))), 23 | // ) 24 | 25 | func NewMultiAddrBalancer(addrs []string) grpc.Balancer { 26 | r := NewHelpResolver(addrs) 27 | return grpc.RoundRobin(r) 28 | } 29 | 30 | // NewHelpResolver creates a new pseudo resolver which returns fixed addrs. 31 | func NewHelpResolver(addrs []string) naming.Resolver { 32 | return &HelpResolver{ 33 | addrs: addrs, 34 | } 35 | } 36 | 37 | type HelpResolver struct { 38 | addrs []string 39 | watcher *helpWatcher 40 | sync.Mutex 41 | } 42 | 43 | // Add dynamic add new target 44 | func (r *HelpResolver) Add(target string) error { 45 | r.Lock() 46 | defer r.Unlock() 47 | 48 | for _, addr := range r.addrs { 49 | if addr == target { 50 | return errors.New("target is existed") 51 | } 52 | } 53 | 54 | updates := []*naming.Update{&naming.Update{Op: naming.Add, Addr: target}} 55 | r.watcher.updatesChan <- updates 56 | return nil 57 | } 58 | 59 | // Resolve 60 | func (r *HelpResolver) Resolve(target string) (naming.Watcher, error) { 61 | r.Lock() 62 | defer r.Unlock() 63 | 64 | w := &helpWatcher{ 65 | updatesChan: make(chan []*naming.Update, 1), 66 | } 67 | updates := []*naming.Update{} 68 | for _, addr := range r.addrs { 69 | updates = append(updates, &naming.Update{Op: naming.Add, Addr: addr}) 70 | } 71 | w.updatesChan <- updates 72 | r.watcher = w 73 | return w, nil 74 | } 75 | 76 | // This watcher is implemented based on ipwatcher below 77 | // https://github.com/grpc/grpc-go/blob/30fb59a4304034ce78ff68e21bd25776b1d79488/naming/dns_resolver.go#L151-L171 78 | type helpWatcher struct { 79 | updatesChan chan []*naming.Update 80 | } 81 | 82 | func (w *helpWatcher) Next() ([]*naming.Update, error) { 83 | us, ok := <-w.updatesChan 84 | if !ok { 85 | return nil, ErrWatcherClose 86 | } 87 | return us, nil 88 | } 89 | 90 | func (w *helpWatcher) Close() { 91 | close(w.updatesChan) 92 | } 93 | -------------------------------------------------------------------------------- /pkcs12utils/mac_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. 2 | // Copyright 2015 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package pkcs12 7 | 8 | import ( 9 | "bytes" 10 | "encoding/asn1" 11 | "testing" 12 | ) 13 | 14 | func TestVerifyMac(t *testing.T) { 15 | td := macData{ 16 | Mac: digestInfo{ 17 | Digest: []byte{0x18, 0x20, 0x3d, 0xff, 0x1e, 0x16, 0xf4, 0x92, 0xf2, 0xaf, 0xc8, 0x91, 0xa9, 0xba, 0xd6, 0xca, 0x9d, 0xee, 0x51, 0x93}, 18 | }, 19 | MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, 20 | Iterations: 2048, 21 | } 22 | 23 | message := []byte{11, 12, 13, 14, 15} 24 | password, _ := bmpString("") 25 | 26 | td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 3}) 27 | err := verifyMac(&td, message, password) 28 | if _, ok := err.(NotImplementedError); !ok { 29 | t.Errorf("err: %v", err) 30 | } 31 | 32 | td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) 33 | err = verifyMac(&td, message, password) 34 | if err != ErrIncorrectPassword { 35 | t.Errorf("Expected incorrect password, got err: %v", err) 36 | } 37 | 38 | password, _ = bmpString("Sesame open") 39 | err = verifyMac(&td, message, password) 40 | if err != nil { 41 | t.Errorf("err: %v", err) 42 | } 43 | 44 | } 45 | 46 | func TestComputeMac(t *testing.T) { 47 | td := macData{ 48 | MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, 49 | Iterations: 2048, 50 | } 51 | 52 | message := []byte{11, 12, 13, 14, 15} 53 | password, _ := bmpString("Sesame open") 54 | 55 | td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 3}) 56 | err := computeMac(&td, message, password) 57 | if _, ok := err.(NotImplementedError); !ok { 58 | t.Errorf("err: %v", err) 59 | } 60 | 61 | td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) 62 | err = computeMac(&td, message, password) 63 | if err != nil { 64 | t.Errorf("err: %v", err) 65 | } 66 | 67 | expectedDigest := []byte{0x18, 0x20, 0x3d, 0xff, 0x1e, 0x16, 0xf4, 0x92, 0xf2, 0xaf, 0xc8, 0x91, 0xa9, 0xba, 0xd6, 0xca, 0x9d, 0xee, 0x51, 0x93} 68 | 69 | if bytes.Compare(td.Mac.Digest, expectedDigest) != 0 { 70 | t.Errorf("Computed incorrect MAC; expected MAC to be '%d' but got '%d'", expectedDigest, td.Mac.Digest) 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /debugutils/aggregator_test.go: -------------------------------------------------------------------------------- 1 | package debugutils 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/golang/mock/gomock" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "github.com/wx-chevalier/go-utils/stringutils" 10 | "github.com/spf13/afero" 11 | ) 12 | 13 | var _ = Describe("aggregator test", func() { 14 | var ( 15 | aggregator *Aggregator 16 | ) 17 | 18 | Context("unit", func() { 19 | var ( 20 | resourceCollector *MockResourceCollector 21 | logCollector *MockLogCollector 22 | storageClient *MockStorageClient 23 | fs afero.Fs 24 | tmpd string 25 | ) 26 | BeforeEach(func() { 27 | var err error 28 | ctrl = gomock.NewController(T) 29 | logCollector = NewMockLogCollector(ctrl) 30 | resourceCollector = NewMockResourceCollector(ctrl) 31 | storageClient = NewMockStorageClient(ctrl) 32 | fs = afero.NewMemMapFs() 33 | tmpd, err = afero.TempDir(fs, "", "") 34 | Expect(err).NotTo(HaveOccurred()) 35 | aggregator = NewAggregator(resourceCollector, logCollector, storageClient, fs, tmpd) 36 | }) 37 | 38 | It("can properly create subdirectories", func() { 39 | directories := []string{"resources", "logs"} 40 | err := aggregator.createSubResourceDirectories() 41 | Expect(err).NotTo(HaveOccurred()) 42 | files, err := afero.ReadDir(fs, tmpd) 43 | Expect(err).NotTo(HaveOccurred()) 44 | Expect(files).To(HaveLen(2)) 45 | for _, v := range files { 46 | Expect(stringutils.ContainsString(filepath.Base(v.Name()), directories)) 47 | } 48 | }) 49 | 50 | It("properly sets all filepaths", func() { 51 | namespace := "ns" 52 | filename := "/hello/world/test.tgz" 53 | resourceCollector.EXPECT().RetrieveResources(gomock.Any(), namespace, gomock.Any()).Return(nil, nil).Times(1) 54 | resourceCollector.EXPECT().SaveResources(storageClient, filepath.Join(tmpd, "resources"), nil).Return(nil).Times(1) 55 | logCollector.EXPECT().GetLogRequests(gomock.Any()).Return(nil, nil).Times(1) 56 | logCollector.EXPECT().SaveLogs(storageClient, filepath.Join(tmpd, "logs"), nil).Times(1) 57 | storageClient.EXPECT().Save(filepath.Dir(filename), gomock.Any()).Return(nil).Times(1) 58 | 59 | err := aggregator.StreamFromManifest(manifests, namespace, filename) 60 | Expect(err).NotTo(HaveOccurred()) 61 | }) 62 | 63 | AfterEach(func() { 64 | ctrl.Finish() 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /configutils/README.md: -------------------------------------------------------------------------------- 1 | This package includes: 2 | - A small client interface for loading and storing kubernetes config maps, with a kube and mock implementation. 3 | - A small wrapper client interface for loading and storing a proto struct from the contents of a config map. 4 | 5 | Example usage: 6 | 7 | In the service mesh hub, we use a config map to store the registries, and have a default config when the config map 8 | is not found. We can create a light wrapper around the config-to-proto client that is strongly typed for the specific 9 | proto message that the hub uses for it's config struct. 10 | 11 | ```go 12 | const ( 13 | ApiserverConfigKey = "config.yaml" 14 | ApiserverConfigMapName = "apiserver-config" 15 | DefaultLogLevel = v1.LogLevel_INFO_LEVEL 16 | ) 17 | 18 | func GetDefaultRegistryGithubLocation() *hubv1.GithubRepositoryLocation { 19 | return &hubv1.GithubRepositoryLocation{ 20 | Org: "wx-chevalier", 21 | Repo: "service-mesh-hub", 22 | Ref: "master", 23 | Directory: "extensions/v1", 24 | } 25 | } 26 | 27 | func GetDefaultApiserverConfig() *v1.ApiserverConfig { 28 | defaultGithub := &v1.Registry_Github{ 29 | Github: GetDefaultRegistryGithubLocation(), 30 | } 31 | return &v1.ApiserverConfig{ 32 | Registries: []*v1.Registry{ 33 | { 34 | Name: "default", 35 | RegistryType: defaultGithub, 36 | }, 37 | }, 38 | LogLevel: DefaultLogLevel, 39 | } 40 | } 41 | 42 | type ConfigClient interface { 43 | GetConfig(ctx context.Context) (*v1.ApiserverConfig, error) 44 | SetConfig(ctx context.Context, config *v1.ApiserverConfig) error 45 | } 46 | 47 | type configClient struct { 48 | delegate configutils.ConfigClient 49 | } 50 | 51 | func NewConfigClient(kube configutils.ConfigMapClient, installNamespace string) ConfigClient { 52 | delegate := configutils.NewConfigClient(kube, installNamespace, ApiserverConfigMapName, ApiserverConfigKey, GetDefaultApiserverConfig()) 53 | return &configClient{ 54 | delegate: delegate, 55 | } 56 | } 57 | 58 | func (c *configClient) GetConfig(ctx context.Context) (*v1.ApiserverConfig, error) { 59 | var config v1.ApiserverConfig 60 | if err := c.delegate.GetConfig(ctx, &config); err != nil { 61 | return nil, err 62 | } 63 | return &config, nil 64 | } 65 | 66 | func (c *configClient) SetConfig(ctx context.Context, config *v1.ApiserverConfig) error { 67 | return c.delegate.SetConfig(ctx, config) 68 | } 69 | ``` -------------------------------------------------------------------------------- /parseutils/parseutil.go: -------------------------------------------------------------------------------- 1 | package parseutil 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/wx-chevalier/go-utils/pointers" 10 | ) 11 | 12 | // ParseBool ... 13 | func ParseBool(userInputStr string) (bool, error) { 14 | if userInputStr == "" { 15 | return false, errors.New("No string to parse") 16 | } 17 | userInputStr = strings.TrimSpace(userInputStr) 18 | 19 | lowercased := strings.ToLower(userInputStr) 20 | if lowercased == "yes" || lowercased == "y" { 21 | return true, nil 22 | } 23 | if lowercased == "no" || lowercased == "n" { 24 | return false, nil 25 | } 26 | return strconv.ParseBool(lowercased) 27 | } 28 | 29 | // CastToString ... 30 | func CastToString(value interface{}) string { 31 | casted, ok := value.(string) 32 | 33 | if !ok { 34 | castedStr := fmt.Sprintf("%v", value) 35 | casted = castedStr 36 | } 37 | 38 | return casted 39 | } 40 | 41 | // CastToStringPtr ... 42 | func CastToStringPtr(value interface{}) *string { 43 | castedValue := CastToString(value) 44 | return pointers.NewStringPtr(castedValue) 45 | } 46 | 47 | // CastToBool ... 48 | func CastToBool(value interface{}) (bool, bool) { 49 | casted, ok := value.(bool) 50 | 51 | if !ok { 52 | castedStr := CastToString(value) 53 | 54 | castedBool, err := ParseBool(castedStr) 55 | if err != nil { 56 | return false, false 57 | } 58 | 59 | casted = castedBool 60 | } 61 | 62 | return casted, true 63 | } 64 | 65 | // CastToBoolPtr ... 66 | func CastToBoolPtr(value interface{}) (*bool, bool) { 67 | castedValue, ok := CastToBool(value) 68 | if !ok { 69 | return nil, false 70 | } 71 | return pointers.NewBoolPtr(castedValue), true 72 | } 73 | 74 | // CastToMapStringInterface ... 75 | func CastToMapStringInterface(value interface{}) (map[string]interface{}, bool) { 76 | castedValue, ok := value.(map[interface{}]interface{}) 77 | desiredMap := map[string]interface{}{} 78 | for key, value := range castedValue { 79 | keyStr, ok := key.(string) 80 | if !ok { 81 | return map[string]interface{}{}, false 82 | } 83 | desiredMap[keyStr] = value 84 | } 85 | return desiredMap, ok 86 | } 87 | 88 | // CastToMapStringInterfacePtr ... 89 | func CastToMapStringInterfacePtr(value interface{}) (*map[string]interface{}, bool) { 90 | casted, ok := CastToMapStringInterface(value) 91 | if !ok { 92 | return nil, false 93 | } 94 | return pointers.NewMapStringInterfacePtr(casted), true 95 | } 96 | -------------------------------------------------------------------------------- /shell/README.md: -------------------------------------------------------------------------------- 1 | ![logo.png](logo.png) 2 | 3 | # go-shell 4 | 5 | easy execute shell, better `os/exec` 6 | 7 | ## Feature 8 | 9 | * simple api 10 | * add timeout 11 | * add stop() 12 | * add yum api 13 | * use channel to send stdout and stderr 14 | * merge stdout and stderr to new output 15 | * use sync.pool to reduce alloc buffer 16 | 17 | ## Usage 18 | 19 | 😁 **Look at the code for yourself** 20 | 21 | ```golang 22 | func TestRunShell(t *testing.T) { 23 | cmd := NewCommand("ls;ls -sdf8;sleep 2;echo 123456") 24 | cmd.Start() 25 | cmd.Wait() 26 | status := cmd.Status 27 | 28 | assert.Equal(t, status.ExitCode, 0) 29 | assert.Equal(t, status.Error, nil) 30 | assert.Equal(t, status.Finish, true) 31 | assert.Greater(t, status.PID, 0) 32 | assert.GreaterOrEqual(t, cmd.Status.CostTime.Seconds(), float64(2)) 33 | } 34 | 35 | func TestCheckOutput(t *testing.T) { 36 | cmd := NewCommand("echo 123123 >&2") 37 | cmd.Run() 38 | status := cmd.Status 39 | 40 | assert.Equal(t, status.Output, "123123\n") 41 | assert.Equal(t, status.Stdout, "") 42 | assert.Equal(t, status.Stderr, "123123\n") 43 | } 44 | 45 | func TestRunTimeout(t *testing.T) { 46 | cmd := NewCommand("echo 123; sleep 5", WithTimeout(2)) 47 | cmd.Start() 48 | cmd.Wait() 49 | status := cmd.Status 50 | 51 | assert.Equal(t, status.Error, ErrProcessTimeout) 52 | assert.Greater(t, status.CostTime.Seconds(), float64(2)) 53 | assert.Less(t, status.CostTime.Seconds(), float64(3)) 54 | } 55 | 56 | func TestCheckStdout(t *testing.T) { 57 | cmd := NewCommand("echo 123123") 58 | cmd.Run() 59 | status := cmd.Status 60 | 61 | assert.Equal(t, status.Stdout, "123123\n") 62 | assert.Equal(t, status.Output, "123123\n") 63 | assert.Equal(t, status.Stderr, "") 64 | } 65 | 66 | func TestCheckStream(t *testing.T) { 67 | stdoutChan := make(chan string, 100) 68 | incr := 0 69 | go func() { 70 | for line := range stdoutChan { 71 | incr++ 72 | fmt.Println(incr, line) 73 | } 74 | }() 75 | 76 | cmd := exec.Command("bash", "-c", "echo 123;sleep 1;echo 456; echo 789") 77 | stdout := NewOutputStream(stdoutChan) 78 | cmd.Stdout = stdout 79 | cmd.Run() 80 | 81 | assert.Equal(t, incr, 3) 82 | } 83 | 84 | func TestCheckBuffer(t *testing.T) { 85 | cmd := exec.Command("bash", "-c", "echo 123") 86 | stdout := NewOutputBuffer() 87 | cmd.Stdout = stdout 88 | cmd.Run() 89 | 90 | assert.Equal(t, stdout.buf.String(), "123\n") 91 | assert.Equal(t, stdout.Lines()[0], "123") 92 | } 93 | ``` 94 | -------------------------------------------------------------------------------- /strutils/freezable.go: -------------------------------------------------------------------------------- 1 | package freezable 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // 8 | // This package implements a `freeze` function, 9 | // similar to Ruby's `freeze` method: http://ruby-doc.org/core-2.3.0/Object.html#method-i-freeze 10 | // Once an object is fozen you can't unfreeze it, and `Set` will return an error 11 | // if called on a frozen object. 12 | // You can check whether the object is frozen with the `IsFrozen` function. 13 | // 14 | 15 | // --- String ---------------------------- 16 | 17 | // String ... 18 | type String struct { 19 | data *string 20 | isFrozen bool 21 | } 22 | 23 | // Set ... 24 | func (freezableObj *String) Set(s string) error { 25 | if freezableObj.isFrozen { 26 | return fmt.Errorf("freezable.String: Object is already frozen. (Current value: %s) (New value was: %s)", 27 | freezableObj.String(), s) 28 | } 29 | 30 | freezableObj.data = &s 31 | return nil 32 | } 33 | 34 | // Freeze ... 35 | func (freezableObj *String) Freeze() { 36 | freezableObj.isFrozen = true 37 | } 38 | 39 | // IsFrozen ... 40 | func (freezableObj *String) IsFrozen() bool { 41 | return freezableObj.isFrozen 42 | } 43 | 44 | // Get ... 45 | func (freezableObj String) Get() string { 46 | if freezableObj.data == nil { 47 | return "" 48 | } 49 | return *freezableObj.data 50 | } 51 | 52 | // String ... 53 | func (freezableObj String) String() string { 54 | return freezableObj.Get() 55 | } 56 | 57 | // --- StringSlice ---------------------------- 58 | 59 | // StringSlice ... 60 | type StringSlice struct { 61 | data *[]string 62 | isFrozen bool 63 | } 64 | 65 | // Set ... 66 | func (freezableObj *StringSlice) Set(s []string) error { 67 | if freezableObj.isFrozen { 68 | return fmt.Errorf("freezable.StringSlice: Object is already frozen. (Current value: %s) (New value was: %s)", 69 | freezableObj.Get(), s) 70 | } 71 | 72 | freezableObj.data = &s 73 | return nil 74 | } 75 | 76 | // Freeze ... 77 | func (freezableObj *StringSlice) Freeze() { 78 | freezableObj.isFrozen = true 79 | } 80 | 81 | // IsFrozen ... 82 | func (freezableObj *StringSlice) IsFrozen() bool { 83 | return freezableObj.isFrozen 84 | } 85 | 86 | // Get ... 87 | func (freezableObj StringSlice) Get() []string { 88 | if freezableObj.data == nil { 89 | return []string{} 90 | } 91 | return *freezableObj.data 92 | } 93 | 94 | // String ... 95 | func (freezableObj StringSlice) String() string { 96 | return fmt.Sprintf("%s", freezableObj.Get()) 97 | } 98 | -------------------------------------------------------------------------------- /kubectrlutils/util_test.go: -------------------------------------------------------------------------------- 1 | package kubeutils 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/ginkgo/extensions/table" 6 | . "github.com/onsi/gomega" 7 | ) 8 | 9 | var _ = Describe("sanitize name", func() { 10 | 11 | DescribeTable("sanitize short names", func(in, out string) { 12 | Expect(SanitizeNameV2(in)).To(Equal(out)) 13 | }, 14 | Entry("basic a", "abc", "abc"), 15 | Entry("basic b", "abc123", "abc123"), 16 | Entry("subX *", "bb*", "bb-"), 17 | Entry("sub *", "bb*b", "bb-b"), 18 | Entry("subX /", "bb/", "bb-"), 19 | Entry("sub /", "bb/b", "bb-b"), 20 | Entry("subX .", "bb.", "bb-"), 21 | Entry("sub .", "bb.b", "bb-b"), 22 | Entry("sub0 [", "bb[", "bb"), 23 | Entry("sub [", "bb[b", "bbb"), 24 | Entry("sub0 ]", "bb]", "bb"), 25 | Entry("sub ]", "bb]b", "bbb"), 26 | Entry("subX :", "bb:", "bb-"), 27 | Entry("sub :", "bb:b", "bb-b"), 28 | Entry("subX space", "bb ", "bb-"), 29 | Entry("sub space", "bb b", "bb-b"), 30 | Entry("subX newline", "bb\n", "bb"), 31 | Entry("sub newline", "bb\nb", "bbb"), 32 | Entry("sub0 quote", "aa\"", "aa"), 33 | Entry("sub quote b", "bb\"b", "bbb"), 34 | Entry("sub0 single quote", "aa'", "aa"), 35 | Entry("sub single quote b", "bb'b", "bbb"), 36 | // these are technically invalid kube names, as are the subX cases, but user should know that and kube wil warn 37 | Entry("invalid a", "123", "123"), 38 | Entry("invalid b", "-abc", "-abc"), 39 | ) 40 | 41 | DescribeTable("sanitize long names", func(in, out string) { 42 | sanitized := SanitizeNameV2(in) 43 | Expect(sanitized).To(Equal(out)) 44 | Expect(len(sanitized)).To(BeNumerically("<=", 63)) 45 | }, 46 | Entry("300a's", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 47 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-4e5475d125a33c6190718e75adc1b70"), 48 | Entry("301a's", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 49 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-c73301b7b71679067b02cff4cdc5e70"), 50 | ) 51 | }) 52 | -------------------------------------------------------------------------------- /surveyutils/input_test.go: -------------------------------------------------------------------------------- 1 | package surveyutils_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | "github.com/wx-chevalier/go-utils/surveyutils" 7 | clitestutils "github.com/wx-chevalier/go-utils/testutils/cli" 8 | ) 9 | 10 | var _ = Describe("GetInput", func() { 11 | Context("bool input", func() { 12 | It("correctly sets the input value", func() { 13 | clitestutils.ExpectInteractive(func(c *clitestutils.Console) { 14 | c.ExpectString("test msg [y/N]: ") 15 | c.SendLine("y") 16 | c.ExpectEOF() 17 | }, func() { 18 | var val bool 19 | err := surveyutils.GetBoolInput("test msg", &val) 20 | Expect(err).NotTo(HaveOccurred()) 21 | Expect(val).To(BeTrue()) 22 | }) 23 | }) 24 | }) 25 | 26 | Context("list select", func() { 27 | var options = []string{"one", "two", "three"} 28 | Context("single select", func() { 29 | It("can select 1 from list", func() { 30 | clitestutils.ExpectInteractive(func(c *clitestutils.Console) { 31 | c.ExpectString("select option") 32 | c.PressDown() 33 | c.SendLine("") 34 | c.ExpectEOF() 35 | }, func() { 36 | var val string 37 | err := surveyutils.ChooseFromList("select option", &val, options) 38 | Expect(err).NotTo(HaveOccurred()) 39 | Expect(val).To(Equal("two")) 40 | }) 41 | }) 42 | }) 43 | Context("multi select", func() { 44 | It("can select one from a mutli-select list", func() { 45 | clitestutils.ExpectInteractive(func(c *clitestutils.Console) { 46 | c.ExpectString("select option") 47 | c.PressDown() 48 | c.Send(" ") 49 | c.SendLine("") 50 | c.ExpectEOF() 51 | }, func() { 52 | var val []string 53 | err := surveyutils.ChooseMultiFromList("select option", &val, options) 54 | Expect(err).NotTo(HaveOccurred()) 55 | Expect(val).To(Equal([]string{"two"})) 56 | }) 57 | }) 58 | 59 | It("can select mutli from a mutli-select list", func() { 60 | clitestutils.ExpectInteractive(func(c *clitestutils.Console) { 61 | c.ExpectString("select option") 62 | c.PressDown() 63 | c.Send(" ") 64 | c.PressDown() 65 | c.Send(" ") 66 | c.SendLine("") 67 | c.ExpectEOF() 68 | }, func() { 69 | var val []string 70 | err := surveyutils.ChooseMultiFromList("select option", &val, options) 71 | Expect(err).NotTo(HaveOccurred()) 72 | Expect(val).To(Equal([]string{"two", "three"})) 73 | }) 74 | }) 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /configutils/config_map_client.go: -------------------------------------------------------------------------------- 1 | package configutils 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wx-chevalier/go-utils/contextutils" 7 | "go.uber.org/zap" 8 | v1 "k8s.io/api/core/v1" 9 | kubeerr "k8s.io/apimachinery/pkg/api/errors" 10 | kubemeta "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/client-go/kubernetes" 12 | ) 13 | 14 | type ConfigMapClient interface { 15 | GetConfigMap(ctx context.Context, namespace string, name string) (*v1.ConfigMap, error) 16 | SetConfigMap(ctx context.Context, config *v1.ConfigMap) error 17 | } 18 | 19 | type KubeConfigMapClient struct { 20 | client kubernetes.Interface 21 | } 22 | 23 | func NewConfigMapClient(client kubernetes.Interface) ConfigMapClient { 24 | return &KubeConfigMapClient{ 25 | client: client, 26 | } 27 | } 28 | 29 | func (c *KubeConfigMapClient) GetConfigMap(ctx context.Context, namespace string, configMapName string) (*v1.ConfigMap, error) { 30 | contextutils.LoggerFrom(ctx).Debugw("Getting config map from Kubernetes", 31 | zap.String("namespace", namespace), 32 | zap.String("name", configMapName)) 33 | configMap, err := c.client.CoreV1().ConfigMaps(namespace).Get(configMapName, kubemeta.GetOptions{}) 34 | if err != nil { 35 | contextutils.LoggerFrom(ctx).Errorw("Could not get config map", 36 | zap.Error(err), 37 | zap.String("name", configMapName), 38 | zap.String("namespace", namespace)) 39 | return nil, err 40 | } 41 | return configMap, nil 42 | } 43 | 44 | func (c *KubeConfigMapClient) SetConfigMap(ctx context.Context, config *v1.ConfigMap) error { 45 | contextutils.LoggerFrom(ctx).Debugw("Setting config map in Kubernetes", 46 | zap.String("namespace", config.Namespace), 47 | zap.String("name", config.Name)) 48 | _, err := c.client.CoreV1().ConfigMaps(config.Namespace).Update(config) 49 | if err != nil { 50 | if !kubeerr.IsNotFound(err) { 51 | contextutils.LoggerFrom(ctx).Errorw("Could not update config map", 52 | zap.Error(err), 53 | zap.String("name", config.Name), 54 | zap.String("namespace", config.Namespace), 55 | zap.Any("configMap", config)) 56 | return err 57 | } 58 | _, err := c.client.CoreV1().ConfigMaps(config.Namespace).Create(config) 59 | if err != nil { 60 | contextutils.LoggerFrom(ctx).Errorw("Config map not found, but error creating it", 61 | zap.Error(err), 62 | zap.String("name", config.Name), 63 | zap.String("namespace", config.Namespace), 64 | zap.Any("configMap", config)) 65 | return err 66 | } 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /fileutils/writter.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | "os" 7 | ) 8 | 9 | // WriteStringToFile ... 10 | func WriteStringToFile(pth string, fileCont string) error { 11 | return WriteBytesToFile(pth, []byte(fileCont)) 12 | } 13 | 14 | // WriteStringToFileWithPermission ... 15 | func WriteStringToFileWithPermission(pth string, fileCont string, perm os.FileMode) error { 16 | return WriteBytesToFileWithPermission(pth, []byte(fileCont), perm) 17 | } 18 | 19 | // WriteBytesToFileWithPermission ... 20 | func WriteBytesToFileWithPermission(pth string, fileCont []byte, perm os.FileMode) error { 21 | if pth == "" { 22 | return errors.New("No path provided") 23 | } 24 | 25 | var file *os.File 26 | var err error 27 | if perm == 0 { 28 | file, err = os.Create(pth) 29 | } else { 30 | // same as os.Create, but with a specified permission 31 | // the flags are copy-pasted from the official 32 | // os.Create func: https://golang.org/src/os/file.go?s=7327:7366#L244 33 | file, err = os.OpenFile(pth, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) 34 | } 35 | if err != nil { 36 | return err 37 | } 38 | defer func() { 39 | if err := file.Close(); err != nil { 40 | log.Println(" [!] Failed to close file:", err) 41 | } 42 | }() 43 | 44 | if _, err := file.Write(fileCont); err != nil { 45 | return err 46 | } 47 | 48 | return nil 49 | } 50 | 51 | // WriteBytesToFile ... 52 | func WriteBytesToFile(pth string, fileCont []byte) error { 53 | return WriteBytesToFileWithPermission(pth, fileCont, 0) 54 | } 55 | 56 | // AppendStringToFile ... 57 | func AppendStringToFile(pth string, fileCont string) error { 58 | return AppendBytesToFile(pth, []byte(fileCont)) 59 | } 60 | 61 | // AppendBytesToFile ... 62 | func AppendBytesToFile(pth string, fileCont []byte) error { 63 | if pth == "" { 64 | return errors.New("No path provided") 65 | } 66 | 67 | var file *os.File 68 | filePerm, err := GetFilePermissions(pth) 69 | if err != nil { 70 | // create the file 71 | file, err = os.Create(pth) 72 | } else { 73 | // open for append 74 | file, err = os.OpenFile(pth, os.O_APPEND|os.O_CREATE|os.O_WRONLY, filePerm) 75 | } 76 | if err != nil { 77 | // failed to create or open-for-append the file 78 | return err 79 | } 80 | defer func() { 81 | if err := file.Close(); err != nil { 82 | log.Println(" [!] Failed to close file:", err) 83 | } 84 | }() 85 | 86 | if _, err := file.Write(fileCont); err != nil { 87 | return err 88 | } 89 | 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /fileutils/tar_test.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "github.com/spf13/afero" 9 | ) 10 | 11 | var _ = Describe("tarutils", func() { 12 | Context("File system", func() { 13 | It("can tar/untar files", func() { 14 | fs := afero.NewOsFs() 15 | tmpDir := mustWriteTestDir(fs) 16 | mustAddTestFiles(tmpDir, fs) 17 | tmp, err := afero.TempFile(fs, "", "tar-zipped-file-") 18 | Expect(err).NotTo(HaveOccurred()) 19 | err = Tar(tmpDir, fs, tmp) 20 | Expect(err).NotTo(HaveOccurred()) 21 | 22 | newTmpDir := mustWriteTestDir(fs) 23 | err = Untar(newTmpDir, tmp.Name(), fs) 24 | Expect(err).NotTo(HaveOccurred()) 25 | 26 | mustFindOriginalFiles(newTmpDir, fs) 27 | }) 28 | }) 29 | 30 | Context("Mem map", func() { 31 | It("can tar/untar files", func() { 32 | fs := afero.NewMemMapFs() 33 | tmpDir := mustWriteTestDir(fs) 34 | mustAddTestFiles(tmpDir, fs) 35 | tmp, err := afero.TempFile(fs, "", "tar-zipped-file-") 36 | Expect(err).NotTo(HaveOccurred()) 37 | err = Tar(tmpDir, fs, tmp) 38 | Expect(err).NotTo(HaveOccurred()) 39 | 40 | newTmpDir := mustWriteTestDir(fs) 41 | err = Untar(newTmpDir, tmp.Name(), fs) 42 | Expect(err).NotTo(HaveOccurred()) 43 | 44 | mustFindOriginalFiles(newTmpDir, fs) 45 | }) 46 | }) 47 | }) 48 | 49 | func mustFindOriginalFiles(newTmpDir string, fs afero.Fs) { 50 | files, err := afero.ReadDir(fs, newTmpDir) 51 | Expect(err).NotTo(HaveOccurred()) 52 | for _, v := range files { 53 | if !v.IsDir() { 54 | _, err := afero.ReadFile(fs, newTmpDir+"/"+v.Name()) 55 | Expect(err).NotTo(HaveOccurred()) 56 | } else { 57 | mustFindOriginalFiles(filepath.Join(newTmpDir, v.Name()), fs) 58 | } 59 | } 60 | } 61 | 62 | func mustWriteTestDir(fs afero.Fs) string { 63 | tmpDir, err := afero.TempDir(fs, "", "tar-test-") 64 | Expect(err).NotTo(HaveOccurred()) 65 | return tmpDir 66 | } 67 | 68 | func mustAddTestFiles(tmpdir string, fs afero.Fs) { 69 | dir, err := afero.TempDir(fs, tmpdir, "tar-test-nested-folder") 70 | Expect(err).NotTo(HaveOccurred()) 71 | file, err := afero.TempFile(fs, tmpdir, "tar-test-file-") 72 | Expect(err).NotTo(HaveOccurred()) 73 | err = afero.WriteFile(fs, file.Name(), []byte("first file"), 0777) 74 | Expect(err).NotTo(HaveOccurred()) 75 | file, err = afero.TempFile(fs, dir, "tar-test-file-") 76 | Expect(err).NotTo(HaveOccurred()) 77 | err = afero.WriteFile(fs, file.Name(), []byte("second file"), 0777) 78 | Expect(err).NotTo(HaveOccurred()) 79 | } 80 | -------------------------------------------------------------------------------- /botutils/server.go: -------------------------------------------------------------------------------- 1 | package botutils 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/wx-chevalier/go-utils/botutils/botconfig" 10 | 11 | "github.com/palantir/go-baseapp/baseapp" 12 | "github.com/palantir/go-githubapp/githubapp" 13 | "github.com/rs/zerolog" 14 | "github.com/wx-chevalier/go-utils/contextutils" 15 | "goji.io/pat" 16 | ) 17 | 18 | type StaticBotConfig struct { 19 | BotName string 20 | Version string 21 | } 22 | 23 | type GitBot interface { 24 | Start(ctx context.Context, plugins ...Plugin) error 25 | } 26 | 27 | // This bot doesn't read any configuration from the repo, just uses the application config and static config provided 28 | type simpleGitBot struct { 29 | staticConfig StaticBotConfig 30 | config *botconfig.Config 31 | } 32 | 33 | func NewSimpleGitBot(staticConfig StaticBotConfig) (GitBot, error) { 34 | config, err := botconfig.ReadConfig() 35 | if err != nil { 36 | return nil, err 37 | } 38 | return &simpleGitBot{ 39 | config: config, 40 | staticConfig: staticConfig, 41 | }, nil 42 | } 43 | 44 | func (b *simpleGitBot) Start(ctx context.Context, plugins ...Plugin) error { 45 | cc, err := githubapp.NewDefaultCachingClientCreator( 46 | b.config.Github, 47 | githubapp.WithClientUserAgent(fmt.Sprintf("%s/%s", b.staticConfig.BotName, b.staticConfig.Version)), 48 | githubapp.WithClientMiddleware( 49 | githubapp.ClientLogging(zerolog.DebugLevel), 50 | ), 51 | ) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | contextutils.LoggerFrom(ctx).Infow(fmt.Sprintf("Hello from %s!", b.staticConfig.BotName)) 57 | // baseapp library requires zerolog, we use zap everywhere else :( 58 | logger := zerolog.Ctx(ctx) 59 | if logger == nil { 60 | tmp := zerolog.New(os.Stdout).With().Timestamp().Logger() 61 | logger = &tmp 62 | } 63 | server, err := baseapp.NewServer( 64 | b.config.Server, 65 | baseapp.DefaultParams(*logger, fmt.Sprintf("%s.", b.staticConfig.BotName))..., 66 | ) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | githubHandler := NewGithubHookHandler(ctx, cc) 72 | for _, p := range plugins { 73 | githubHandler.RegisterPlugin(p) 74 | } 75 | webhookHandler := githubapp.NewDefaultEventDispatcher(b.config.Github, githubHandler) 76 | server.Mux().Handle(pat.Post(githubapp.DefaultWebhookRoute), webhookHandler) 77 | server.Mux().Handle(pat.New("/"), _200ok()) 78 | 79 | // Start is blocking 80 | return server.Start() 81 | } 82 | 83 | func _200ok() http.Handler { 84 | return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) 85 | } 86 | -------------------------------------------------------------------------------- /kubeinstallutils/kubeinstall/installer_callbacks.go: -------------------------------------------------------------------------------- 1 | package kubeinstall 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 5 | ) 6 | 7 | type CallbackOptions interface { 8 | PreInstall() error 9 | PostInstall() error 10 | PreCreate(res *unstructured.Unstructured) error 11 | PostCreate(res *unstructured.Unstructured) error 12 | PreUpdate(res *unstructured.Unstructured) error 13 | PostUpdate(res *unstructured.Unstructured) error 14 | PreDelete(res *unstructured.Unstructured) error 15 | PostDelete(res *unstructured.Unstructured) error 16 | } 17 | 18 | type CallbackOption struct { 19 | OnPreInstall func() error 20 | OnPostInstall func() error 21 | OnPreCreate func(res *unstructured.Unstructured) error 22 | OnPostCreate func(res *unstructured.Unstructured) error 23 | OnPreUpdate func(res *unstructured.Unstructured) error 24 | OnPostUpdate func(res *unstructured.Unstructured) error 25 | OnPreDelete func(res *unstructured.Unstructured) error 26 | OnPostDelete func(res *unstructured.Unstructured) error 27 | } 28 | 29 | func (cb *CallbackOption) PreInstall() error { 30 | if cb.OnPreInstall != nil { 31 | return cb.OnPreInstall() 32 | } 33 | return nil 34 | } 35 | 36 | func (cb *CallbackOption) PostInstall() error { 37 | if cb.OnPostInstall != nil { 38 | return cb.OnPostInstall() 39 | } 40 | return nil 41 | } 42 | 43 | func (cb *CallbackOption) PreCreate(res *unstructured.Unstructured) error { 44 | if cb.OnPreCreate != nil { 45 | return cb.OnPreCreate(res) 46 | } 47 | return nil 48 | } 49 | 50 | func (cb *CallbackOption) PostCreate(res *unstructured.Unstructured) error { 51 | if cb.OnPostCreate != nil { 52 | return cb.OnPostCreate(res) 53 | } 54 | return nil 55 | } 56 | 57 | func (cb *CallbackOption) PreUpdate(res *unstructured.Unstructured) error { 58 | if cb.OnPreUpdate != nil { 59 | return cb.OnPreUpdate(res) 60 | } 61 | return nil 62 | } 63 | 64 | func (cb *CallbackOption) PostUpdate(res *unstructured.Unstructured) error { 65 | if cb.OnPostUpdate != nil { 66 | return cb.OnPostUpdate(res) 67 | } 68 | return nil 69 | } 70 | 71 | func (cb *CallbackOption) PreDelete(res *unstructured.Unstructured) error { 72 | if cb.OnPreDelete != nil { 73 | return cb.OnPreDelete(res) 74 | } 75 | return nil 76 | } 77 | 78 | func (cb *CallbackOption) PostDelete(res *unstructured.Unstructured) error { 79 | if cb.OnPostDelete != nil { 80 | return cb.OnPostDelete(res) 81 | } 82 | return nil 83 | } 84 | 85 | func initCallbacks() []CallbackOptions { 86 | return []CallbackOptions{ 87 | &CallbackOption{OnPreCreate: setInstallationAnnotation}, 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /kubesetuputils/install_kube_manifest_test.go: -------------------------------------------------------------------------------- 1 | package kubeinstallutils_test 2 | 3 | import ( 4 | "os" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "github.com/wx-chevalier/go-utils/kubeinstallutils" 9 | "github.com/wx-chevalier/go-utils/kubeutils" 10 | "github.com/wx-chevalier/go-utils/testutils" 11 | "github.com/wx-chevalier/go-utils/testutils/kube" 12 | "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 13 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | "k8s.io/client-go/kubernetes" 15 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 16 | ) 17 | 18 | var _ = Describe("InstallKubeManifest", func() { 19 | var ( 20 | namespace string 21 | kubeClient kubernetes.Interface 22 | ) 23 | BeforeEach(func() { 24 | if os.Getenv("RUN_KUBE_TESTS") != "1" { 25 | Skip("use RUN_KUBE_TESTS to run this test") 26 | } 27 | namespace = "install-kube-manifest-" + testutils.RandString(8) 28 | kubeClient = kube.MustKubeClient() 29 | err := kubeutils.CreateNamespacesInParallel(kubeClient, namespace) 30 | Expect(err).NotTo(HaveOccurred()) 31 | }) 32 | AfterEach(func() { 33 | if kubeClient != nil { 34 | err := kubeutils.DeleteNamespacesInParallelBlocking(kubeClient, namespace) 35 | Expect(err).NotTo(HaveOccurred()) 36 | } 37 | }) 38 | It("installs arbitrary kube manifests", func() { 39 | err := deployNginx(namespace) 40 | Expect(err).NotTo(HaveOccurred()) 41 | 42 | cfg, err := kubeutils.GetConfig("", "") 43 | Expect(err).NotTo(HaveOccurred()) 44 | kube, err := kubernetes.NewForConfig(cfg) 45 | Expect(err).NotTo(HaveOccurred()) 46 | 47 | svcs, err := kube.CoreV1().Services(namespace).List(v1.ListOptions{}) 48 | Expect(err).NotTo(HaveOccurred()) 49 | deployments, err := kube.ExtensionsV1beta1().Deployments(namespace).List(v1.ListOptions{}) 50 | Expect(err).NotTo(HaveOccurred()) 51 | Expect(svcs.Items).To(HaveLen(1)) 52 | Expect(deployments.Items).To(HaveLen(1)) 53 | 54 | }) 55 | }) 56 | 57 | func deployNginx(namespace string) error { 58 | cfg, err := kubeutils.GetConfig("", "") 59 | if err != nil { 60 | return err 61 | } 62 | kube, err := kubernetes.NewForConfig(cfg) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | apiext, err := clientset.NewForConfig(cfg) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | installer := kubeinstallutils.NewKubeInstaller(kube, apiext, namespace) 73 | 74 | kubeObjs, err := kubeinstallutils.ParseKubeManifest(testutils.NginxYaml) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | for _, kubeOjb := range kubeObjs { 80 | if err := installer.Create(kubeOjb); err != nil { 81 | return err 82 | } 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /testutils/trimmed_stack_fail_handler.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "os" 8 | "regexp" 9 | "runtime/debug" 10 | ) 11 | 12 | //PrintTrimmedStack helps you find the line of the failing assertion without producing excessive noise. 13 | // This is achieved by printing a stack trace and pruning lines associated with known overhead. 14 | // With this fail handler, you do not need to count stack offsets ExpectWithOffset(x, ...) and can just Expect(...) 15 | func PrintTrimmedStack() { 16 | stack := debug.Stack() 17 | fmt.Println(trimVendorStack(stack)) 18 | } 19 | func trimVendorStack(stack []byte) string { 20 | scanner := bufio.NewScanner(bytes.NewReader(stack)) 21 | ind := -1 22 | pair := []string{} 23 | skipCount := 0 24 | output := "" 25 | for scanner.Scan() { 26 | ind++ 27 | if ind == 0 { 28 | // skip the header 29 | continue 30 | } 31 | pair = append(pair, scanner.Text()) 32 | if len(pair) == 2 { 33 | evaluateStackPair(pair[0], pair[1], &output, &skipCount) 34 | pair = []string{} 35 | } 36 | } 37 | if err := scanner.Err(); err != nil { 38 | fmt.Fprintln(os.Stderr, "reading standard input:", err) 39 | } 40 | output = fmt.Sprintf("Stack trace (skipped %v entries that matched filter criteria):\n%v", skipCount, output) 41 | return output 42 | } 43 | 44 | var ( 45 | funcRuntimeDebugRegex = ®exp.Regexp{} 46 | fileVendorRegex = ®exp.Regexp{} 47 | fileGoModRegex = ®exp.Regexp{} 48 | fileSuiteRegex = ®exp.Regexp{} 49 | fileGoTestLibRegex = ®exp.Regexp{} 50 | fileSelfDescription = ®exp.Regexp{} 51 | ) 52 | 53 | func init() { 54 | funcRuntimeDebugRegex = regexp.MustCompile("runtime/debug") 55 | fileVendorRegex = regexp.MustCompile("vendor") 56 | fileGoModRegex = regexp.MustCompile("/go/pkg/mod/") 57 | fileSuiteRegex = regexp.MustCompile("suite_test.go") 58 | fileGoTestLibRegex = regexp.MustCompile("src/testing/testing.go") 59 | fileSelfDescription = regexp.MustCompile("wx-chevalier/go-utils/testutils/trimmed") 60 | } 61 | 62 | func evaluateStackPair(functionLine, fileLine string, output *string, skipCount *int) { 63 | skip := false 64 | if funcRuntimeDebugRegex.MatchString(functionLine) { 65 | skip = true 66 | } 67 | if fileVendorRegex.MatchString(fileLine) || 68 | fileGoModRegex.MatchString(fileLine) || 69 | fileSuiteRegex.MatchString(fileLine) || 70 | fileGoTestLibRegex.MatchString(fileLine) || 71 | fileSelfDescription.MatchString(fileLine) { 72 | skip = true 73 | } 74 | if skip { 75 | *skipCount = *skipCount + 1 76 | return 77 | } 78 | *output = fmt.Sprintf("%v%v\n%v\n", *output, functionLine, fileLine) 79 | return 80 | } 81 | --------------------------------------------------------------------------------