Compare commits
20 Commits
v3.3.0-rc.
...
v3.3.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac36ff5752 | ||
|
|
098b77fb4c | ||
|
|
e97f27e580 | ||
|
|
bc00b67a6e | ||
|
|
8b0f2674bd | ||
|
|
108963f87b | ||
|
|
6525a3c3b3 | ||
|
|
f0cc7f6430 | ||
|
|
47563af08c | ||
|
|
26b871ecf4 | ||
|
|
5e02f1b86b | ||
|
|
c78ab9039a | ||
|
|
02e99365c7 | ||
|
|
0c2a419a5e | ||
|
|
77e0373777 | ||
|
|
04d70b1db4 | ||
|
|
86beabdb32 | ||
|
|
1e8cea4971 | ||
|
|
107e2ec64c | ||
|
|
17b97d7ada |
@@ -82,6 +82,9 @@ type KubeSphereControllerManagerOptions struct {
|
||||
// * has the lowest priority.
|
||||
// e.g. *,-foo, means "disable 'foo'"
|
||||
ControllerGates []string
|
||||
|
||||
// Enable gops or not.
|
||||
GOPSEnabled bool
|
||||
}
|
||||
|
||||
func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions {
|
||||
@@ -144,6 +147,9 @@ func (s *KubeSphereControllerManagerOptions) Flags(allControllerNameSelectors []
|
||||
"named 'foo', '-foo' disables the controller named 'foo'.\nAll controllers: %s",
|
||||
strings.Join(allControllerNameSelectors, ", ")))
|
||||
|
||||
gfs.BoolVar(&s.GOPSEnabled, "gops", s.GOPSEnabled, "Whether to enable gops or not. When enabled this option, "+
|
||||
"controller-manager will listen on a random port on 127.0.0.1, then you can use the gops tool to list and diagnose the controller-manager currently running.")
|
||||
|
||||
kfs := fss.FlagSet("klog")
|
||||
local := flag.NewFlagSet("klog", flag.ExitOnError)
|
||||
klog.InitFlags(local)
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/google/gops/agent"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
@@ -73,12 +74,21 @@ func NewControllerManagerCommand() *cobra.Command {
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "controller-manager",
|
||||
Long: `KubeSphere controller manager is a daemon that`,
|
||||
Long: `KubeSphere controller manager is a daemon that embeds the control loops shipped with KubeSphere.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if errs := s.Validate(allControllers); len(errs) != 0 {
|
||||
klog.Error(utilerrors.NewAggregate(errs))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if s.GOPSEnabled {
|
||||
// Add agent to report additional information such as the current stack trace, Go version, memory stats, etc.
|
||||
// Bind to a random port on address 127.0.0.1
|
||||
if err := agent.Listen(agent.Options{}); err != nil {
|
||||
klog.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = Run(s, controllerconfig.WatchConfigChange(), signals.SetupSignalHandler()); err != nil {
|
||||
klog.Error(err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -62,6 +62,9 @@ type ServerRunOptions struct {
|
||||
|
||||
//
|
||||
DebugMode bool
|
||||
|
||||
// Enable gops or not.
|
||||
GOPSEnabled bool
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
@@ -76,6 +79,8 @@ func NewServerRunOptions() *ServerRunOptions {
|
||||
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||
fs := fss.FlagSet("generic")
|
||||
fs.BoolVar(&s.DebugMode, "debug", false, "Don't enable this if you don't know what it means.")
|
||||
fs.BoolVar(&s.GOPSEnabled, "gops", false, "Whether to enable gops or not. When enabled this option, "+
|
||||
"ks-apiserver will listen on a random port on 127.0.0.1, then you can use the gops tool to list and diagnose the ks-apiserver currently running.")
|
||||
s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions)
|
||||
s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions)
|
||||
s.AuthenticationOptions.AddFlags(fss.FlagSet("authentication"), s.AuthenticationOptions)
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/gops/agent"
|
||||
"github.com/spf13/cobra"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
@@ -57,6 +58,15 @@ cluster's shared state through which all other components interact.`,
|
||||
if errs := s.Validate(); len(errs) != 0 {
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
if s.GOPSEnabled {
|
||||
// Add agent to report additional information such as the current stack trace, Go version, memory stats, etc.
|
||||
// Bind to a random port on address 127.0.0.1.
|
||||
if err := agent.Listen(agent.Options{}); err != nil {
|
||||
klog.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return Run(s, apiserverconfig.WatchConfigChange(), signals.SetupSignalHandler())
|
||||
},
|
||||
SilenceUsage: true,
|
||||
|
||||
3
go.mod
3
go.mod
@@ -50,6 +50,7 @@ require (
|
||||
github.com/golang/example v0.0.0-20170904185048-46695d81d1fa
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/google/go-containerregistry v0.6.0
|
||||
github.com/google/gops v0.3.23
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gorilla/handlers v1.4.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
@@ -81,6 +82,8 @@ require (
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/prometheus/common v0.26.0
|
||||
github.com/prometheus/prometheus v1.8.2-0.20200907175821-8219b442c864
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7 // indirect
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
|
||||
github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
|
||||
github.com/speps/go-hashids v2.0.0+incompatible
|
||||
github.com/spf13/cobra v1.2.1
|
||||
|
||||
12
go.sum
12
go.sum
@@ -59,6 +59,7 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/O
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
@@ -287,6 +288,8 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM=
|
||||
github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.2.6-0.20210915003542-8b1f7f90f6b1/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/analysis v0.19.10 h1:5BHISBAXOc/aJK25irLZnx2D3s6WyYaY9D4gmuz9fdE=
|
||||
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
|
||||
github.com/go-openapi/errors v0.19.4 h1:fSGwO1tSYHFu70NKaWJt5Qh0qoBRtCm/mXS1yhf+0W0=
|
||||
@@ -388,6 +391,8 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gops v0.3.23 h1:OjsHRINl5FiIyTc8jivIg4UN0GY6Nh32SL8KRbl8GQo=
|
||||
github.com/google/gops v0.3.23/go.mod h1:7diIdLsqpCihPSX3fQagksT/Ku/y4RL9LHTlKyEUDl8=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20200417002340-c6e0a841f49a/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
@@ -501,6 +506,7 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
@@ -741,6 +747,9 @@ github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP
|
||||
github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.9/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -784,6 +793,8 @@ github.com/thanos-io/thanos v0.13.1-0.20200910143741-e0b7f7b32e9c/go.mod h1:1Ize
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
@@ -991,6 +1002,7 @@ k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
|
||||
kubesphere.io/monitoring-dashboard v0.2.2 h1:aniATtXLgRAAvKOjd2UxWWHMh4/T7a0HoQ9bd+/bGcA=
|
||||
kubesphere.io/monitoring-dashboard v0.2.2/go.mod h1:ksDjmOuoN0C0GuYp0s5X3186cPgk2asLUaO1WlEKISY=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
|
||||
rsc.io/letsencrypt v0.0.1 h1:DV0d09Ne9E7UUa9ZqWktZ9L2VmybgTgfq7xlfFR/bbU=
|
||||
rsc.io/letsencrypt v0.0.1/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -196,13 +196,13 @@ func newDeployments(deploymentName, namespace string, labels map[string]string,
|
||||
return deployment
|
||||
}
|
||||
|
||||
func newService(serviceName, namesapce string, labels map[string]string) *corev1.Service {
|
||||
func newService(serviceName, namespace string, labels map[string]string) *corev1.Service {
|
||||
labels["app"] = serviceName
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: serviceName,
|
||||
Namespace: namesapce,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
Annotations: map[string]string{
|
||||
"servicemesh.kubesphere.io/enabled": "true",
|
||||
|
||||
@@ -184,7 +184,7 @@ func NewClusterController(
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
oldCluster := oldObj.(*clusterv1alpha1.Cluster)
|
||||
newCluster := newObj.(*clusterv1alpha1.Cluster)
|
||||
if !reflect.DeepEqual(oldCluster.Spec, newCluster.Spec) {
|
||||
if !reflect.DeepEqual(oldCluster.Spec, newCluster.Spec) || newCluster.DeletionTimestamp != nil {
|
||||
c.enqueueCluster(newObj)
|
||||
}
|
||||
},
|
||||
@@ -297,10 +297,10 @@ func (c *clusterController) resyncClusters() error {
|
||||
}
|
||||
|
||||
for _, cluster := range clusters {
|
||||
if err = c.syncCluster(cluster.Name); err != nil {
|
||||
klog.Warningf("failed to sync cluster %s: %s", cluster.Name, err)
|
||||
}
|
||||
key, _ := cache.MetaNamespaceKeyFunc(cluster)
|
||||
c.queue.Add(key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
@@ -268,7 +269,7 @@ func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface
|
||||
|
||||
klog.V(2).Infof("Creating service account in joining cluster: %s", joiningClusterName)
|
||||
|
||||
saName, err := createServiceAccount(joiningClusterClientset, namespace,
|
||||
saName, err := createServiceAccountWithSecret(joiningClusterClientset, namespace,
|
||||
joiningClusterName, hostClusterName, dryRun, errorOnExisting)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("Error creating service account: %s in joining cluster: %s due to: %v",
|
||||
@@ -320,31 +321,75 @@ func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface
|
||||
return saName, nil
|
||||
}
|
||||
|
||||
// createServiceAccount creates a service account in the cluster associated
|
||||
// createServiceAccountWithSecret creates a service account and secret in the cluster associated
|
||||
// with clusterClientset with credentials that will be used by the host cluster
|
||||
// to access its API server.
|
||||
func createServiceAccount(clusterClientset kubeclient.Interface, namespace,
|
||||
func createServiceAccountWithSecret(clusterClientset kubeclient.Interface, namespace,
|
||||
joiningClusterName, hostClusterName string, dryRun, errorOnExisting bool) (string, error) {
|
||||
saName := util.ClusterServiceAccountName(joiningClusterName, hostClusterName)
|
||||
sa := &corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: saName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
return saName, nil
|
||||
}
|
||||
|
||||
// Create a new service account.
|
||||
_, err := clusterClientset.CoreV1().ServiceAccounts(namespace).Create(context.Background(), sa, metav1.CreateOptions{})
|
||||
switch {
|
||||
case apierrors.IsAlreadyExists(err) && errorOnExisting:
|
||||
klog.V(2).Infof("Service account %s/%s already exists in target cluster %s", namespace, saName, joiningClusterName)
|
||||
ctx := context.Background()
|
||||
sa, err := clusterClientset.CoreV1().ServiceAccounts(namespace).Get(ctx, saName, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
sa = &corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: saName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
// We must create the sa first, then create the associated secret, and update the sa at last.
|
||||
// Or the kube-controller-manager will delete the secret.
|
||||
sa, err = clusterClientset.CoreV1().ServiceAccounts(namespace).Create(ctx, sa, metav1.CreateOptions{})
|
||||
switch {
|
||||
case apierrors.IsAlreadyExists(err) && errorOnExisting:
|
||||
klog.V(2).Infof("Service account %s/%s already exists in target cluster %s", namespace, saName, joiningClusterName)
|
||||
return "", err
|
||||
case err != nil && !apierrors.IsAlreadyExists(err):
|
||||
klog.V(2).Infof("Could not create service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err)
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if len(sa.Secrets) > 0 {
|
||||
return saName, nil
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: fmt.Sprintf("%s-token-", saName),
|
||||
Namespace: namespace,
|
||||
Annotations: map[string]string{
|
||||
corev1.ServiceAccountNameKey: saName,
|
||||
},
|
||||
},
|
||||
Type: corev1.SecretTypeServiceAccountToken,
|
||||
}
|
||||
|
||||
// After kubernetes v1.24, kube-controller-manger will not create the default secret for
|
||||
// service account. http://kep.k8s.io/2800
|
||||
// Create a default secret.
|
||||
secret, err = clusterClientset.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{})
|
||||
|
||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
klog.V(2).Infof("Could not create secret for service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err)
|
||||
return "", err
|
||||
case err != nil && !apierrors.IsAlreadyExists(err):
|
||||
klog.V(2).Infof("Could not create service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err)
|
||||
}
|
||||
|
||||
// At last, update the service account.
|
||||
sa.Secrets = append(sa.Secrets, corev1.ObjectReference{Name: secret.Name})
|
||||
_, err = clusterClientset.CoreV1().ServiceAccounts(namespace).Update(ctx, sa, metav1.UpdateOptions{})
|
||||
switch {
|
||||
case err != nil:
|
||||
klog.Infof("Could not update service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err)
|
||||
return "", err
|
||||
default:
|
||||
return saName, nil
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -177,7 +176,7 @@ func (h *handler) PodLog(request *restful.Request, response *restful.Response) {
|
||||
}
|
||||
|
||||
fw := flushwriter.Wrap(response.ResponseWriter)
|
||||
err := h.gw.GetPodLogs(context.TODO(), podNamespace, podID, logOptions, fw)
|
||||
err := h.gw.GetPodLogs(request.Request.Context(), podNamespace, podID, logOptions, fw)
|
||||
if err != nil {
|
||||
api.HandleError(response, request, err)
|
||||
return
|
||||
@@ -196,7 +195,7 @@ func (h *handler) PodLogSearch(request *restful.Request, response *restful.Respo
|
||||
api.HandleError(response, request, err)
|
||||
return
|
||||
}
|
||||
// ES log will be filted by pods and namespace by default.
|
||||
// ES log will be filtered by pods and namespace by default.
|
||||
pods, err := h.gw.GetPods(ns, &query.Query{})
|
||||
if err != nil {
|
||||
api.HandleError(response, request, err)
|
||||
|
||||
@@ -753,7 +753,7 @@ func (h *openpitrixHandler) ListApplications(req *restful.Request, resp *restful
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(result)
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *openpitrixHandler) UpgradeApplication(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
142
pkg/kapis/servicemesh/metrics/v1alpha2/handler_test.go
Normal file
142
pkg/kapis/servicemesh/metrics/v1alpha2/handler_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
fakek8s "k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/klog"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/kiali"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/servicemesh"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
)
|
||||
|
||||
func prepare() (*Handler, error) {
|
||||
var namespaceName = "kubesphere-system"
|
||||
var serviceAccountName = "kubesphere"
|
||||
var secretName = "kiali"
|
||||
clientset := fakek8s.NewSimpleClientset()
|
||||
|
||||
ctx := context.Background()
|
||||
namespacesClient := clientset.CoreV1().Namespaces()
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespaceName,
|
||||
},
|
||||
}
|
||||
_, err := namespacesClient.Create(ctx, ns, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
klog.Errorf("create namespace failed ")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: namespaceName,
|
||||
},
|
||||
}
|
||||
|
||||
object := &corev1.ObjectReference{
|
||||
Name: secretName,
|
||||
}
|
||||
|
||||
sa := &corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: serviceAccountName,
|
||||
Namespace: namespaceName,
|
||||
},
|
||||
Secrets: []corev1.ObjectReference{*object},
|
||||
}
|
||||
|
||||
serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespaceName)
|
||||
|
||||
_, err = serviceAccountClient.Create(ctx, sa, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
klog.Errorf("create serviceAccount failed ")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretClient := clientset.CoreV1().Secrets(namespaceName)
|
||||
|
||||
_, err = secretClient.Create(ctx, secret, metav1.CreateOptions{})
|
||||
|
||||
if err != nil {
|
||||
klog.Errorf("create secret failed ")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mock jaeger server
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
options := &servicemesh.Options{
|
||||
IstioPilotHost: "",
|
||||
KialiQueryHost: "",
|
||||
JaegerQueryHost: ts.URL,
|
||||
ServicemeshPrometheusHost: "",
|
||||
}
|
||||
handler := NewHandler(options, clientset, nil)
|
||||
|
||||
token, _ := json.Marshal(
|
||||
&kiali.TokenResponse{
|
||||
Username: "test",
|
||||
Token: "test",
|
||||
},
|
||||
)
|
||||
|
||||
mc := &kiali.MockClient{
|
||||
TokenResult: token,
|
||||
RequestResult: "fake",
|
||||
}
|
||||
|
||||
client := kiali.NewClient("token", nil, mc, "token", options.KialiQueryHost)
|
||||
|
||||
err = reflectutils.SetUnExportedField(handler, "client", client)
|
||||
if err != nil {
|
||||
klog.Errorf("apply mock client failed")
|
||||
return nil, err
|
||||
}
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func TestGetServiceTracing(t *testing.T) {
|
||||
handler, err := prepare()
|
||||
if err != nil {
|
||||
t.Fatalf("init handler failed")
|
||||
}
|
||||
|
||||
namespaceName := "namespace-test"
|
||||
serviceName := "service-test"
|
||||
url := fmt.Sprintf("/namespaces/%s/services/%s/traces", namespaceName, serviceName)
|
||||
request, _ := http.NewRequest("GET", url, nil)
|
||||
query := request.URL.Query()
|
||||
query.Add("start", "1650167872000000")
|
||||
query.Add("end", "1650211072000000")
|
||||
query.Add("limit", "10")
|
||||
request.URL.RawQuery = query.Encode()
|
||||
|
||||
restfulRequest := restful.NewRequest(request)
|
||||
pathMap := make(map[string]string)
|
||||
pathMap["namespace"] = namespaceName
|
||||
pathMap["service"] = serviceName
|
||||
if err := reflectutils.SetUnExportedField(restfulRequest, "pathParameters", pathMap); err != nil {
|
||||
t.Fatalf("set pathParameters failed")
|
||||
}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
restfulResponse := restful.NewResponse(recorder)
|
||||
restfulResponse.SetRequestAccepts("application/json")
|
||||
handler.GetServiceTracing(restfulRequest, restfulResponse)
|
||||
if status := restfulResponse.StatusCode(); status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ type GatewayOperator interface {
|
||||
UpdateGateway(namespace string, obj *v1alpha1.Gateway) (*v1alpha1.Gateway, error)
|
||||
UpgradeGateway(namespace string) (*v1alpha1.Gateway, error)
|
||||
ListGateways(query *query.Query) (*api.ListResult, error)
|
||||
GetPods(namesapce string, query *query.Query) (*api.ListResult, error)
|
||||
GetPods(namespace string, query *query.Query) (*api.ListResult, error)
|
||||
GetPodLogs(ctx context.Context, namespace string, podName string, logOptions *corev1.PodLogOptions, responseWriter io.Writer) error
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func NewGatewayOperator(client client.Client, cache cache.Cache, options *gatewa
|
||||
|
||||
func (c *gatewayOperator) getWorkingNamespace(namespace string) string {
|
||||
ns := c.options.Namespace
|
||||
// Set the working namespace to watching namespace when the Gatway's Namsapce Option is empty
|
||||
// Set the working namespace to watching namespace when the Gateway's Namespace Option is empty
|
||||
if ns == "" {
|
||||
ns = namespace
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func (c *gatewayOperator) DeleteGateway(namespace string) error {
|
||||
// Update Gateway
|
||||
func (c *gatewayOperator) UpdateGateway(namespace string, obj *v1alpha1.Gateway) (*v1alpha1.Gateway, error) {
|
||||
if c.options.Namespace == "" && obj.Namespace != namespace || c.options.Namespace != "" && c.options.Namespace != obj.Namespace {
|
||||
return nil, fmt.Errorf("namepsace doesn't match with origin namesapce")
|
||||
return nil, fmt.Errorf("namespace doesn't match with origin namespace")
|
||||
}
|
||||
c.overrideDefaultValue(obj, namespace)
|
||||
err := c.client.Update(context.TODO(), obj)
|
||||
@@ -345,7 +345,7 @@ func (c *gatewayOperator) UpgradeGateway(namespace string) (*v1alpha1.Gateway, e
|
||||
}()
|
||||
}
|
||||
|
||||
// Delete old deployment, because it's not compatile with the deployment in the helm chart.
|
||||
// Delete old deployment, because it's not compatible with the deployment in the helm chart.
|
||||
// We can't defer here, there's a potential race condition causing gateway operator fails.
|
||||
d := &appsv1.Deployment{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -451,7 +451,7 @@ func (c *gatewayOperator) compare(left runtime.Object, right runtime.Object, fie
|
||||
|
||||
func (c *gatewayOperator) filter(object runtime.Object, filter query.Filter) bool {
|
||||
var objMeta v1.ObjectMeta
|
||||
var namesapce string
|
||||
var namespace string
|
||||
|
||||
gateway, ok := object.(*v1alpha1.Gateway)
|
||||
if !ok {
|
||||
@@ -459,31 +459,31 @@ func (c *gatewayOperator) filter(object runtime.Object, filter query.Filter) boo
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
namesapce = svc.Labels["project"]
|
||||
namespace = svc.Labels["project"]
|
||||
objMeta = svc.ObjectMeta
|
||||
} else {
|
||||
namesapce = gateway.Spec.Controller.Scope.Namespace
|
||||
namespace = gateway.Spec.Controller.Scope.Namespace
|
||||
objMeta = gateway.ObjectMeta
|
||||
}
|
||||
|
||||
switch filter.Field {
|
||||
case query.FieldNamespace:
|
||||
return strings.Compare(namesapce, string(filter.Value)) == 0
|
||||
return strings.Compare(namespace, string(filter.Value)) == 0
|
||||
default:
|
||||
return v1alpha3.DefaultObjectMetaFilter(objMeta, filter)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) GetPods(namesapce string, query *query.Query) (*api.ListResult, error) {
|
||||
func (c *gatewayOperator) GetPods(namespace string, query *query.Query) (*api.ListResult, error) {
|
||||
podGetter := pod.New(c.factory.KubernetesSharedInformerFactory())
|
||||
|
||||
//TODO: move the selector string to options
|
||||
selector, err := labels.Parse(fmt.Sprintf("app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/instance=kubesphere-router-%s-ingress", namesapce))
|
||||
selector, err := labels.Parse(fmt.Sprintf("app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/instance=kubesphere-router-%s-ingress", namespace))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invild selector config")
|
||||
return nil, fmt.Errorf("invaild selector config")
|
||||
}
|
||||
query.LabelSelector = selector.String()
|
||||
return podGetter.List(c.getWorkingNamespace(namesapce), query)
|
||||
return podGetter.List(c.getWorkingNamespace(namespace), query)
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) GetPodLogs(ctx context.Context, namespace string, podName string, logOptions *corev1.PodLogOptions, responseWriter io.Writer) error {
|
||||
|
||||
@@ -92,7 +92,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
namespace: "projct1",
|
||||
namespace: "project1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -105,7 +105,7 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
namespace: "projct1",
|
||||
namespace: "project1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -336,7 +336,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
namespace: "projct1",
|
||||
namespace: "project1",
|
||||
obj: &v1alpha1.Gateway{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "Gateway",
|
||||
@@ -346,7 +346,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
Controller: v1alpha1.ControllerSpec{
|
||||
Scope: v1alpha1.Scope{
|
||||
Enabled: true,
|
||||
Namespace: "projct1",
|
||||
Namespace: "project1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -367,7 +367,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
namespace: "projct2",
|
||||
namespace: "project2",
|
||||
obj: &v1alpha1.Gateway{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "Gateway",
|
||||
@@ -377,7 +377,7 @@ func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
Controller: v1alpha1.ControllerSpec{
|
||||
Scope: v1alpha1.Scope{
|
||||
Enabled: true,
|
||||
Namespace: "projct2",
|
||||
Namespace: "project2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -593,7 +593,7 @@ func Test_gatewayOperator_UpgradeGateway(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
namespace: "projct1",
|
||||
namespace: "project1",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
@@ -220,7 +220,7 @@ func (o *operator) createCSR(username string) error {
|
||||
}
|
||||
|
||||
var csrBuffer, keyBuffer bytes.Buffer
|
||||
if err = pem.Encode(&keyBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(x509key)}); err != nil {
|
||||
if err = pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(x509key)}); err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -58,7 +57,7 @@ func TestClient_Get(t *testing.T) {
|
||||
Strategy: AuthStrategyAnonymous,
|
||||
cache: nil,
|
||||
client: &MockClient{
|
||||
requestResult: "fake",
|
||||
RequestResult: "fake",
|
||||
},
|
||||
ServiceToken: "token",
|
||||
Host: "http://kiali.istio-system.svc",
|
||||
@@ -76,8 +75,8 @@ func TestClient_Get(t *testing.T) {
|
||||
Strategy: AuthStrategyToken,
|
||||
cache: nil,
|
||||
client: &MockClient{
|
||||
tokenResult: token,
|
||||
requestResult: "fake",
|
||||
TokenResult: token,
|
||||
RequestResult: "fake",
|
||||
},
|
||||
ServiceToken: "token",
|
||||
Host: "http://kiali.istio-system.svc",
|
||||
@@ -95,8 +94,8 @@ func TestClient_Get(t *testing.T) {
|
||||
Strategy: AuthStrategyToken,
|
||||
cache: cache.NewSimpleCache(),
|
||||
client: &MockClient{
|
||||
tokenResult: token,
|
||||
requestResult: "fake",
|
||||
TokenResult: token,
|
||||
RequestResult: "fake",
|
||||
},
|
||||
ServiceToken: "token",
|
||||
Host: "http://kiali.istio-system.svc",
|
||||
@@ -129,22 +128,3 @@ func TestClient_Get(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type MockClient struct {
|
||||
tokenResult []byte
|
||||
requestResult string
|
||||
}
|
||||
|
||||
func (c *MockClient) Do(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(c.requestResult))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *MockClient) PostForm(url string, data url.Values) (resp *http.Response, err error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(c.tokenResult)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
27
pkg/simple/client/kiali/mock_client.go
Normal file
27
pkg/simple/client/kiali/mock_client.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package kiali
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type MockClient struct {
|
||||
TokenResult []byte
|
||||
RequestResult string
|
||||
}
|
||||
|
||||
func (c *MockClient) Do(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(c.RequestResult))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *MockClient) PostForm(url string, data url.Values) (resp *http.Response, err error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(c.TokenResult)),
|
||||
}, nil
|
||||
}
|
||||
@@ -17,7 +17,9 @@ limitations under the License.
|
||||
package reflectutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func In(value interface{}, container interface{}) bool {
|
||||
@@ -60,3 +62,15 @@ func Override(left interface{}, right interface{}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetUnExportedField(ptr interface{}, filedName string, newFiledValue interface{}) (err error) {
|
||||
v := reflect.ValueOf(ptr).Elem().FieldByName(filedName)
|
||||
v = reflect.NewAt(v.Type(), unsafe.Pointer(v.UnsafeAddr())).Elem()
|
||||
nv := reflect.ValueOf(newFiledValue)
|
||||
|
||||
if v.Kind() != nv.Kind() {
|
||||
return fmt.Errorf("kind error")
|
||||
}
|
||||
v.Set(nv)
|
||||
return nil
|
||||
}
|
||||
|
||||
27
vendor/github.com/google/gops/LICENSE
generated
vendored
Normal file
27
vendor/github.com/google/gops/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2016 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
285
vendor/github.com/google/gops/agent/agent.go
generated
vendored
Normal file
285
vendor/github.com/google/gops/agent/agent.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package agent provides hooks programs can register to retrieve
|
||||
// diagnostics data by using gops.
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
gosignal "os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/google/gops/internal"
|
||||
"github.com/google/gops/signal"
|
||||
)
|
||||
|
||||
const defaultAddr = "127.0.0.1:0"
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
portfile string
|
||||
listener net.Listener
|
||||
|
||||
units = []string{" bytes", "KB", "MB", "GB", "TB", "PB"}
|
||||
)
|
||||
|
||||
// Options allows configuring the started agent.
|
||||
type Options struct {
|
||||
// Addr is the host:port the agent will be listening at.
|
||||
// Optional.
|
||||
Addr string
|
||||
|
||||
// ConfigDir is the directory to store the configuration file,
|
||||
// PID of the gops process, filename, port as well as content.
|
||||
// Optional.
|
||||
ConfigDir string
|
||||
|
||||
// ShutdownCleanup automatically cleans up resources if the
|
||||
// running process receives an interrupt. Otherwise, users
|
||||
// can call Close before shutting down.
|
||||
// Optional.
|
||||
ShutdownCleanup bool
|
||||
|
||||
// ReuseSocketAddrAndPort determines whether the SO_REUSEADDR and
|
||||
// SO_REUSEPORT socket options should be set on the listening socket of
|
||||
// the agent. This option is only effective on unix-like OSes and if
|
||||
// Addr is set to a fixed host:port.
|
||||
// Optional.
|
||||
ReuseSocketAddrAndPort bool
|
||||
}
|
||||
|
||||
// Listen starts the gops agent on a host process. Once agent started, users
|
||||
// can use the advanced gops features. The agent will listen to Interrupt
|
||||
// signals and exit the process, if you need to perform further work on the
|
||||
// Interrupt signal use the options parameter to configure the agent
|
||||
// accordingly.
|
||||
//
|
||||
// Note: The agent exposes an endpoint via a TCP connection that can be used by
|
||||
// any program on the system. Review your security requirements before starting
|
||||
// the agent.
|
||||
func Listen(opts Options) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if portfile != "" {
|
||||
return fmt.Errorf("gops: agent already listening at: %v", listener.Addr())
|
||||
}
|
||||
|
||||
// new
|
||||
gopsdir := opts.ConfigDir
|
||||
if gopsdir == "" {
|
||||
cfgDir, err := internal.ConfigDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gopsdir = cfgDir
|
||||
}
|
||||
|
||||
err := os.MkdirAll(gopsdir, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.ShutdownCleanup {
|
||||
gracefulShutdown()
|
||||
}
|
||||
|
||||
addr := opts.Addr
|
||||
if addr == "" {
|
||||
addr = defaultAddr
|
||||
}
|
||||
var lc net.ListenConfig
|
||||
if opts.ReuseSocketAddrAndPort {
|
||||
lc.Control = setReuseAddrAndPortSockopts
|
||||
}
|
||||
listener, err = lc.Listen(context.Background(), "tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
port := listener.Addr().(*net.TCPAddr).Port
|
||||
portfile = filepath.Join(gopsdir, strconv.Itoa(os.Getpid()))
|
||||
err = ioutil.WriteFile(portfile, []byte(strconv.Itoa(port)), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go listen()
|
||||
return nil
|
||||
}
|
||||
|
||||
func listen() {
|
||||
buf := make([]byte, 1)
|
||||
for {
|
||||
fd, err := listener.Accept()
|
||||
if err != nil {
|
||||
// No great way to check for this, see https://golang.org/issues/4373.
|
||||
if !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
fmt.Fprintf(os.Stderr, "gops: %v\n", err)
|
||||
}
|
||||
if netErr, ok := err.(net.Error); ok && !netErr.Temporary() {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, err := fd.Read(buf); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gops: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if err := handle(fd, buf); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gops: %v\n", err)
|
||||
continue
|
||||
}
|
||||
fd.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func gracefulShutdown() {
|
||||
c := make(chan os.Signal, 1)
|
||||
gosignal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
go func() {
|
||||
// cleanup the socket on shutdown.
|
||||
sig := <-c
|
||||
Close()
|
||||
ret := 1
|
||||
if sig == syscall.SIGTERM {
|
||||
ret = 0
|
||||
}
|
||||
os.Exit(ret)
|
||||
}()
|
||||
}
|
||||
|
||||
// Close closes the agent, removing temporary files and closing the TCP listener.
|
||||
// If no agent is listening, Close does nothing.
|
||||
func Close() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if portfile != "" {
|
||||
os.Remove(portfile)
|
||||
portfile = ""
|
||||
}
|
||||
if listener != nil {
|
||||
listener.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func formatBytes(val uint64) string {
|
||||
var i int
|
||||
var target uint64
|
||||
for i = range units {
|
||||
target = 1 << uint(10*(i+1))
|
||||
if val < target {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i > 0 {
|
||||
return fmt.Sprintf("%0.2f%s (%d bytes)", float64(val)/(float64(target)/1024), units[i], val)
|
||||
}
|
||||
return fmt.Sprintf("%d bytes", val)
|
||||
}
|
||||
|
||||
func handle(conn io.ReadWriter, msg []byte) error {
|
||||
switch msg[0] {
|
||||
case signal.StackTrace:
|
||||
return pprof.Lookup("goroutine").WriteTo(conn, 2)
|
||||
case signal.GC:
|
||||
runtime.GC()
|
||||
_, err := conn.Write([]byte("ok"))
|
||||
return err
|
||||
case signal.MemStats:
|
||||
var s runtime.MemStats
|
||||
runtime.ReadMemStats(&s)
|
||||
fmt.Fprintf(conn, "alloc: %v\n", formatBytes(s.Alloc))
|
||||
fmt.Fprintf(conn, "total-alloc: %v\n", formatBytes(s.TotalAlloc))
|
||||
fmt.Fprintf(conn, "sys: %v\n", formatBytes(s.Sys))
|
||||
fmt.Fprintf(conn, "lookups: %v\n", s.Lookups)
|
||||
fmt.Fprintf(conn, "mallocs: %v\n", s.Mallocs)
|
||||
fmt.Fprintf(conn, "frees: %v\n", s.Frees)
|
||||
fmt.Fprintf(conn, "heap-alloc: %v\n", formatBytes(s.HeapAlloc))
|
||||
fmt.Fprintf(conn, "heap-sys: %v\n", formatBytes(s.HeapSys))
|
||||
fmt.Fprintf(conn, "heap-idle: %v\n", formatBytes(s.HeapIdle))
|
||||
fmt.Fprintf(conn, "heap-in-use: %v\n", formatBytes(s.HeapInuse))
|
||||
fmt.Fprintf(conn, "heap-released: %v\n", formatBytes(s.HeapReleased))
|
||||
fmt.Fprintf(conn, "heap-objects: %v\n", s.HeapObjects)
|
||||
fmt.Fprintf(conn, "stack-in-use: %v\n", formatBytes(s.StackInuse))
|
||||
fmt.Fprintf(conn, "stack-sys: %v\n", formatBytes(s.StackSys))
|
||||
fmt.Fprintf(conn, "stack-mspan-inuse: %v\n", formatBytes(s.MSpanInuse))
|
||||
fmt.Fprintf(conn, "stack-mspan-sys: %v\n", formatBytes(s.MSpanSys))
|
||||
fmt.Fprintf(conn, "stack-mcache-inuse: %v\n", formatBytes(s.MCacheInuse))
|
||||
fmt.Fprintf(conn, "stack-mcache-sys: %v\n", formatBytes(s.MCacheSys))
|
||||
fmt.Fprintf(conn, "other-sys: %v\n", formatBytes(s.OtherSys))
|
||||
fmt.Fprintf(conn, "gc-sys: %v\n", formatBytes(s.GCSys))
|
||||
fmt.Fprintf(conn, "next-gc: when heap-alloc >= %v\n", formatBytes(s.NextGC))
|
||||
lastGC := "-"
|
||||
if s.LastGC != 0 {
|
||||
lastGC = fmt.Sprint(time.Unix(0, int64(s.LastGC)))
|
||||
}
|
||||
fmt.Fprintf(conn, "last-gc: %v\n", lastGC)
|
||||
fmt.Fprintf(conn, "gc-pause-total: %v\n", time.Duration(s.PauseTotalNs))
|
||||
fmt.Fprintf(conn, "gc-pause: %v\n", s.PauseNs[(s.NumGC+255)%256])
|
||||
fmt.Fprintf(conn, "gc-pause-end: %v\n", s.PauseEnd[(s.NumGC+255)%256])
|
||||
fmt.Fprintf(conn, "num-gc: %v\n", s.NumGC)
|
||||
fmt.Fprintf(conn, "num-forced-gc: %v\n", s.NumForcedGC)
|
||||
fmt.Fprintf(conn, "gc-cpu-fraction: %v\n", s.GCCPUFraction)
|
||||
fmt.Fprintf(conn, "enable-gc: %v\n", s.EnableGC)
|
||||
fmt.Fprintf(conn, "debug-gc: %v\n", s.DebugGC)
|
||||
case signal.Version:
|
||||
fmt.Fprintf(conn, "%v\n", runtime.Version())
|
||||
case signal.HeapProfile:
|
||||
return pprof.WriteHeapProfile(conn)
|
||||
case signal.CPUProfile:
|
||||
if err := pprof.StartCPUProfile(conn); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(30 * time.Second)
|
||||
pprof.StopCPUProfile()
|
||||
case signal.Stats:
|
||||
fmt.Fprintf(conn, "goroutines: %v\n", runtime.NumGoroutine())
|
||||
fmt.Fprintf(conn, "OS threads: %v\n", pprof.Lookup("threadcreate").Count())
|
||||
fmt.Fprintf(conn, "GOMAXPROCS: %v\n", runtime.GOMAXPROCS(0))
|
||||
fmt.Fprintf(conn, "num CPU: %v\n", runtime.NumCPU())
|
||||
case signal.BinaryDump:
|
||||
path, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = bufio.NewReader(f).WriteTo(conn)
|
||||
return err
|
||||
case signal.Trace:
|
||||
if err := trace.Start(conn); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
trace.Stop()
|
||||
case signal.SetGCPercent:
|
||||
perc, err := binary.ReadVarint(bufio.NewReader(conn))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(conn, "New GC percent set to %v. Previous value was %v.\n", perc, debug.SetGCPercent(int(perc)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
37
vendor/github.com/google/gops/agent/sockopt_reuseport.go
generated
vendored
Normal file
37
vendor/github.com/google/gops/agent/sockopt_reuseport.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !js && !plan9 && !solaris && !windows
|
||||
// +build !js,!plan9,!solaris,!windows
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// setReuseAddrAndPortSockopts sets the SO_REUSEADDR and SO_REUSEPORT socket
|
||||
// options on c's underlying socket in order to increase the chance to re-bind()
|
||||
// to the same address and port upon agent restart.
|
||||
func setReuseAddrAndPortSockopts(network, address string, c syscall.RawConn) error {
|
||||
var soerr error
|
||||
if err := c.Control(func(su uintptr) {
|
||||
sock := int(su)
|
||||
// Allow reuse of recently-used addresses. This socket option is
|
||||
// set by default on listeners in Go's net package, see
|
||||
// net.setDefaultSockopts.
|
||||
soerr = unix.SetsockoptInt(sock, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
|
||||
if soerr != nil {
|
||||
return
|
||||
}
|
||||
// Allow reuse of recently-used ports. This gives the agent a
|
||||
// better chance to re-bind upon restarts.
|
||||
soerr = unix.SetsockoptInt(sock, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return soerr
|
||||
}
|
||||
14
vendor/github.com/google/gops/agent/sockopt_unsupported.go
generated
vendored
Normal file
14
vendor/github.com/google/gops/agent/sockopt_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (js && wasm) || plan9 || solaris || windows
|
||||
// +build js,wasm plan9 solaris windows
|
||||
|
||||
package agent
|
||||
|
||||
import "syscall"
|
||||
|
||||
func setReuseAddrAndPortSockopts(network, address string, c syscall.RawConn) error {
|
||||
return nil
|
||||
}
|
||||
71
vendor/github.com/google/gops/internal/internal.go
generated
vendored
Normal file
71
vendor/github.com/google/gops/internal/internal.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const gopsConfigDirEnvKey = "GOPS_CONFIG_DIR"
|
||||
|
||||
func ConfigDir() (string, error) {
|
||||
if configDir := os.Getenv(gopsConfigDirEnvKey); configDir != "" {
|
||||
return configDir, nil
|
||||
}
|
||||
|
||||
if osUserConfigDir := getOSUserConfigDir(); osUserConfigDir != "" {
|
||||
return filepath.Join(osUserConfigDir, "gops"), nil
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
return filepath.Join(os.Getenv("APPDATA"), "gops"), nil
|
||||
}
|
||||
|
||||
if xdgConfigDir := os.Getenv("XDG_CONFIG_HOME"); xdgConfigDir != "" {
|
||||
return filepath.Join(xdgConfigDir, "gops"), nil
|
||||
}
|
||||
|
||||
homeDir := guessUnixHomeDir()
|
||||
if homeDir == "" {
|
||||
return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
|
||||
}
|
||||
return filepath.Join(homeDir, ".config", "gops"), nil
|
||||
}
|
||||
|
||||
func guessUnixHomeDir() string {
|
||||
usr, err := user.Current()
|
||||
if err == nil {
|
||||
return usr.HomeDir
|
||||
}
|
||||
return os.Getenv("HOME")
|
||||
}
|
||||
|
||||
func PIDFile(pid int) (string, error) {
|
||||
gopsdir, err := ConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(gopsdir, strconv.Itoa(pid)), nil
|
||||
}
|
||||
|
||||
func GetPort(pid int) (string, error) {
|
||||
portfile, err := PIDFile(pid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b, err := ioutil.ReadFile(portfile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
port := strings.TrimSpace(string(b))
|
||||
return port, nil
|
||||
}
|
||||
20
vendor/github.com/google/gops/internal/internal_go1_13.go
generated
vendored
Normal file
20
vendor/github.com/google/gops/internal/internal_go1_13.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func getOSUserConfigDir() string {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return configDir
|
||||
}
|
||||
12
vendor/github.com/google/gops/internal/internal_lt_go1_13.go
generated
vendored
Normal file
12
vendor/github.com/google/gops/internal/internal_lt_go1_13.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
package internal
|
||||
|
||||
func getOSUserConfigDir() string {
|
||||
return ""
|
||||
}
|
||||
38
vendor/github.com/google/gops/signal/signal.go
generated
vendored
Normal file
38
vendor/github.com/google/gops/signal/signal.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package signal contains signals used to communicate to the gops agents.
|
||||
package signal
|
||||
|
||||
const (
|
||||
// StackTrace represents a command to print stack trace.
|
||||
StackTrace = byte(0x1)
|
||||
|
||||
// GC runs the garbage collector.
|
||||
GC = byte(0x2)
|
||||
|
||||
// MemStats reports memory stats.
|
||||
MemStats = byte(0x3)
|
||||
|
||||
// Version prints the Go version.
|
||||
Version = byte(0x4)
|
||||
|
||||
// HeapProfile starts `go tool pprof` with the current memory profile.
|
||||
HeapProfile = byte(0x5)
|
||||
|
||||
// CPUProfile starts `go tool pprof` with the current CPU profile
|
||||
CPUProfile = byte(0x6)
|
||||
|
||||
// Stats returns Go runtime statistics such as number of goroutines, GOMAXPROCS, and NumCPU.
|
||||
Stats = byte(0x7)
|
||||
|
||||
// Trace starts the Go execution tracer, waits 5 seconds and launches the trace tool.
|
||||
Trace = byte(0x8)
|
||||
|
||||
// BinaryDump returns running binary file.
|
||||
BinaryDump = byte(0x9)
|
||||
|
||||
// SetGCPercent sets the garbage collection target percentage.
|
||||
SetGCPercent = byte(0x10)
|
||||
)
|
||||
13
vendor/modules.txt
vendored
13
vendor/modules.txt
vendored
@@ -416,6 +416,11 @@ github.com/google/go-containerregistry/pkg/v1/types
|
||||
github.com/google/go-querystring/query
|
||||
# github.com/google/gofuzz v1.1.0 => github.com/google/gofuzz v1.1.0
|
||||
github.com/google/gofuzz
|
||||
# github.com/google/gops v0.3.23
|
||||
## explicit
|
||||
github.com/google/gops/agent
|
||||
github.com/google/gops/internal
|
||||
github.com/google/gops/signal
|
||||
# github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 => github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/shlex
|
||||
# github.com/google/uuid v1.1.2 => github.com/google/uuid v1.1.1
|
||||
@@ -813,6 +818,10 @@ github.com/rubenv/sql-migrate/sqlparse
|
||||
github.com/russross/blackfriday
|
||||
# github.com/sergi/go-diff v1.1.0 => github.com/sergi/go-diff v1.0.0
|
||||
github.com/sergi/go-diff/diffmatchpatch
|
||||
# github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7
|
||||
## explicit
|
||||
# github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4
|
||||
## explicit
|
||||
# github.com/sirupsen/logrus v1.8.1 => github.com/sirupsen/logrus v1.4.2
|
||||
github.com/sirupsen/logrus
|
||||
# github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009 => github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
|
||||
@@ -871,7 +880,7 @@ github.com/xeipuuv/gojsonreference
|
||||
github.com/xeipuuv/gojsonschema
|
||||
# github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656 => github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656
|
||||
## explicit
|
||||
# github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca => github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6
|
||||
# github.com/xlab/treeprint v1.1.0 => github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6
|
||||
github.com/xlab/treeprint
|
||||
# github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b => github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b
|
||||
github.com/yashtewari/glob-intersection
|
||||
@@ -988,7 +997,7 @@ golang.org/x/oauth2/internal
|
||||
golang.org/x/sync/errgroup
|
||||
golang.org/x/sync/semaphore
|
||||
golang.org/x/sync/singleflight
|
||||
# golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 => golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e
|
||||
# golang.org/x/sys v0.0.0-20210902050250-f475640dd07b => golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/plan9
|
||||
golang.org/x/sys/unix
|
||||
|
||||
Reference in New Issue
Block a user