3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "vendor/github.com/knative/pkg"]
|
||||
path = vendor/github.com/knative/pkg
|
||||
url = https://github.com/knative/pkg.git
|
||||
@@ -12,19 +12,19 @@ go:
|
||||
go_import_path: kubesphere.io/kubesphere
|
||||
|
||||
before_install:
|
||||
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
|
||||
before_script:
|
||||
- dep ensure -v
|
||||
- docker --version
|
||||
- bash hack/install_kubebuilder.sh
|
||||
|
||||
script:
|
||||
- make test
|
||||
- bash hack/docker_build.sh
|
||||
- make all && make test && bash hack/docker_build.sh
|
||||
|
||||
deploy:
|
||||
skip_cleanup: true
|
||||
provider: script
|
||||
script: bash hack/docker_push.sh
|
||||
on:
|
||||
branch: master
|
||||
branch: master
|
||||
|
||||
768
Gopkg.lock
generated
768
Gopkg.lock
generated
File diff suppressed because it is too large
Load Diff
129
Gopkg.toml
129
Gopkg.toml
@@ -1,93 +1,68 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
required = [
|
||||
"github.com/emicklei/go-restful",
|
||||
"github.com/onsi/ginkgo", # for test framework
|
||||
"github.com/onsi/gomega", # for test matchers
|
||||
"k8s.io/gengo/examples/defaulter-gen/generators",
|
||||
"k8s.io/gengo/examples/deepcopy-gen/generators",
|
||||
"k8s.io/client-go/plugin/pkg/client/auth/gcp", # for development against gcp
|
||||
"k8s.io/code-generator/cmd/client-gen",
|
||||
"sigs.k8s.io/controller-tools/cmd/controller-gen", # for crd/rbac generation
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config",
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller",
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler",
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager",
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/signals",
|
||||
"sigs.k8s.io/controller-runtime/pkg/source",
|
||||
"sigs.k8s.io/testing_frameworks/integration", # for integration testing
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1",
|
||||
]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/docker"
|
||||
version = "v17.05.0-ce"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/emicklei/go-restful"
|
||||
version = "2.7.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/emicklei/go-restful-swagger12"
|
||||
version = "1.0.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/glog"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/pflag"
|
||||
version = "1.0.1"
|
||||
[[override]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.13.1"
|
||||
|
||||
[[override]]
|
||||
name = "gopkg.in/igm/sockjs-go.v2"
|
||||
version = "2.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
version = "2.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.12.3"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.12.3"
|
||||
version = "kubernetes-1.13.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/code-generator"
|
||||
version = "kubernetes-1.13.1"
|
||||
|
||||
[[override]]
|
||||
name = "k8s.io/client-go"
|
||||
version = "kubernetes-1.12.3"
|
||||
version = "kubernetes-1.13.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/kubernetes"
|
||||
version = "1.12.3"
|
||||
version = "1.13.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mholt/caddy"
|
||||
version = "v0.11.5"
|
||||
name="sigs.k8s.io/controller-runtime"
|
||||
version="v0.1.7"
|
||||
|
||||
[[constraint]]
|
||||
name = "sigs.k8s.io/controller-runtime"
|
||||
version = "v0.1.9"
|
||||
name="sigs.k8s.io/controller-tools"
|
||||
version="v0.1.7"
|
||||
|
||||
[[override]]
|
||||
name = "sigs.k8s.io/controller-tools"
|
||||
version = "v0.1.9"
|
||||
|
||||
[[override]]
|
||||
name = "k8s.io/component-base"
|
||||
branch = "master"
|
||||
name="github.com/bifurcation/mint"
|
||||
revision="824af65410658916142a7600349144e1289f2110"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
non-go = true
|
||||
|
||||
[[prune.project]]
|
||||
name = "k8s.io/code-generator"
|
||||
unused-packages = false
|
||||
non-go = false
|
||||
|
||||
|
||||
# To use reference package:
|
||||
# vendor/github.com/docker/docker/client/container_commit.go:17: undefined: reference.ParseNormalizedNamed
|
||||
@@ -106,20 +81,24 @@
|
||||
name = "github.com/docker/go-connections"
|
||||
branch = "master"
|
||||
|
||||
# To use reference package:
|
||||
# vendor/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go:104:3: unknown field 'CaseSensitive' in struct literal of type jsoniter.Config
|
||||
[[override]]
|
||||
name = "github.com/json-iterator/go"
|
||||
branch = "master"
|
||||
|
||||
|
||||
# For dependency below: Refer to issue https://github.com/golang/dep/issues/1799
|
||||
[[override]]
|
||||
name = "gopkg.in/fsnotify.v1"
|
||||
source = "https://github.com/fsnotify/fsnotify.git"
|
||||
version = "v1.4.7"
|
||||
|
||||
|
||||
[[override]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
version = "v1.5.2"
|
||||
version = "v1.5.2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "sigs.k8s.io/application"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kiali/kiali"
|
||||
version = "0.15.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/mux"
|
||||
version = "1.7.0"
|
||||
|
||||
26
Makefile
26
Makefile
@@ -8,18 +8,6 @@ BIN ?= ks-apiserver
|
||||
IMG ?= kubespheredev/ks-apiserver
|
||||
OUTPUT_DIR=bin
|
||||
|
||||
define get_diff_files
|
||||
$(eval DIFF_FILES=$(shell git diff --name-only --diff-filter=ad | grep -E "^(test|cmd|pkg)/.+\.go"))
|
||||
endef
|
||||
define get_build_flags
|
||||
$(eval SHORT_VERSION=$(shell git describe --tags --always --dirty="-dev"))
|
||||
$(eval SHA1_VERSION=$(shell git show --quiet --pretty=format:%H))
|
||||
$(eval DATE=$(shell date +'%Y-%m-%dT%H:%M:%S'))
|
||||
$(eval BUILD_FLAG= -X $(TRAG.Version).ShortVersion="$(SHORT_VERSION)" \
|
||||
-X $(TRAG.Version).GitSha1Version="$(SHA1_VERSION)" \
|
||||
-X $(TRAG.Version).BuildDate="$(DATE)")
|
||||
endef
|
||||
|
||||
define ALL_HELP_INFO
|
||||
# Build code.
|
||||
#
|
||||
@@ -62,6 +50,18 @@ fmt:
|
||||
vet:
|
||||
go vet ./pkg/... ./cmd/...
|
||||
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
manifests:
|
||||
go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go all
|
||||
|
||||
deploy: manifests
|
||||
kubectl apply -f config/crds
|
||||
kustomize build config/default | kubectl apply -f -
|
||||
|
||||
# Generate DeepCopy to implement runtime.Object
|
||||
deepcopy:
|
||||
./vendor/k8s.io/code-generator/generate-groups.sh deepcopy kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "servicemesh:v1alpha2"
|
||||
|
||||
# Generate code
|
||||
generate:
|
||||
ifndef GOPATH
|
||||
@@ -75,7 +75,7 @@ docker-build: all
|
||||
|
||||
# Run tests
|
||||
test: generate fmt vet
|
||||
go test ./pkg/... ./cmd/... -coverprofile cover.out
|
||||
export KUBEBUILDER_CONTROLPLANE_START_TIMEOUT=1m; go test ./pkg/... ./cmd/... -coverprofile cover.out
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
||||
18
build/controller-manager/Dockerfile
Normal file
18
build/controller-manager/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2018 The KubeSphere Authors. All rights reserved.
|
||||
# Use of this source code is governed by a Apache license
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
# Copyright 2018 The KubeSphere Authors. All rights reserved.
|
||||
# Use of this source code is governed by a Apache license
|
||||
# that can be found in the LICENSE file.
|
||||
FROM golang:1.10.3 as controller-manager-builder
|
||||
|
||||
COPY / /go/src/kubesphere.io/kubesphere
|
||||
WORKDIR /go/src/kubesphere.io/kubesphere
|
||||
|
||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build --ldflags "-extldflags -static" -o controller-manager ./cmd/controller-manager/
|
||||
|
||||
FROM alpine:3.7
|
||||
RUN apk add --update ca-certificates && update-ca-certificates
|
||||
COPY --from=controller-manager-builder /go/src/kubesphere.io/kubesphere/controller-manager /usr/local/bin/
|
||||
CMD controller-manager
|
||||
@@ -20,11 +20,14 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"os"
|
||||
"sigs.k8s.io/application/pkg/apis/app/v1beta1"
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"kubesphere.io/kubesphere/pkg/simple/controller/namespace"
|
||||
"os"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
|
||||
@@ -66,6 +69,13 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.Info("Print all known types")
|
||||
for k, v := range mgr.GetScheme().AllKnownTypes() {
|
||||
if k.Group == v1beta1.SchemeGroupVersion.Group {
|
||||
log.Info(k.String() + " / " + v.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Setup all Controllers
|
||||
log.Info("Setting up controller")
|
||||
if err := controller.AddToManager(mgr); err != nil {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/monitoring/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/operations/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/resources/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/servicemesh/metrics/install"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
34
cmd/ks-apiserver/app/options/options.go
Normal file
34
cmd/ks-apiserver/app/options/options.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
genericoptions "kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
type ServerRunOptions struct {
|
||||
GenericServerRunOptions *genericoptions.ServerRunOptions
|
||||
|
||||
// istio pilot discovery service url
|
||||
IstioPilotServiceURL string
|
||||
OpenPitrixServer string
|
||||
OpenPitrixProxyToken string
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
|
||||
s := ServerRunOptions{
|
||||
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
||||
IstioPilotServiceURL: "http://istio-pilot.istio-system.svc:8080/version",
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
|
||||
s.GenericServerRunOptions.AddFlags(fs)
|
||||
|
||||
fs.StringVar(&s.IstioPilotServiceURL, "istio-pilot-service-url", "http://istio-pilot.istio-system.svc:8080/version", "istio pilot discovery service url")
|
||||
fs.StringVar(&s.OpenPitrixServer, "openpitrix-server", "http://openpitrix-api-gateway.openpitrix-system.svc", "openpitrix server")
|
||||
fs.StringVar(&s.OpenPitrixProxyToken, "openpitrix-proxy-token", "", "openpitrix proxy token")
|
||||
}
|
||||
@@ -21,18 +21,23 @@ import (
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
kconfig "github.com/kiali/kiali/config"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/cmd/ks-apiserver/app/options"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/filter"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
"kubesphere.io/kubesphere/pkg/models/applications"
|
||||
"kubesphere.io/kubesphere/pkg/signals"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/prometheus"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func NewAPIServerCommand() *cobra.Command {
|
||||
s := options.SharedOptions
|
||||
s := options.NewServerRunOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ks-apiserver",
|
||||
@@ -44,14 +49,22 @@ cluster's shared state through which all other components interact.`,
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(s.CommandLine)
|
||||
s.AddFlags(cmd.Flags())
|
||||
cmd.Flags().AddGoFlagSet(goflag.CommandLine)
|
||||
glog.CopyStandardLogTo("INFO")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func Run(s *options.ServerRunOptions) error {
|
||||
|
||||
pflag.VisitAll(func(flag *pflag.Flag) {
|
||||
log.Printf("FLAG: --%s=%q", flag.Name, flag.Value)
|
||||
})
|
||||
|
||||
applications.OpenPitrixServer = s.OpenPitrixServer
|
||||
applications.OpenPitrixProxyToken = s.OpenPitrixProxyToken
|
||||
|
||||
var err error
|
||||
|
||||
waitForResourceSync()
|
||||
@@ -59,19 +72,55 @@ func Run(s *options.ServerRunOptions) error {
|
||||
container := runtime.Container
|
||||
container.Filter(filter.Logging)
|
||||
|
||||
log.Printf("Server listening on %d.", s.InsecurePort)
|
||||
log.Printf("Server listening on %d.", s.GenericServerRunOptions.InsecurePort)
|
||||
|
||||
if s.InsecurePort != 0 {
|
||||
err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.BindAddress, s.InsecurePort), container)
|
||||
for _, webservice := range container.RegisteredWebServices() {
|
||||
for _, route := range webservice.Routes() {
|
||||
log.Printf(route.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if s.SecurePort != 0 && len(s.TlsCertFile) > 0 && len(s.TlsPrivateKey) > 0 {
|
||||
err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.BindAddress, s.SecurePort), s.TlsCertFile, s.TlsPrivateKey, container)
|
||||
initializeKialiConfig(s)
|
||||
|
||||
if s.GenericServerRunOptions.InsecurePort != 0 {
|
||||
err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort), container)
|
||||
}
|
||||
|
||||
if s.GenericServerRunOptions.SecurePort != 0 && len(s.GenericServerRunOptions.TlsCertFile) > 0 && len(s.GenericServerRunOptions.TlsPrivateKey) > 0 {
|
||||
err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort), s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey, container)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func initializeKialiConfig(s *options.ServerRunOptions) {
|
||||
// Initialize kiali config
|
||||
config := kconfig.NewConfig()
|
||||
|
||||
// Exclude system namespaces
|
||||
config.API.Namespaces.Exclude = []string{"istio-system", "kubesphere*", "kube*"}
|
||||
config.InCluster = false
|
||||
|
||||
// Set default prometheus service url
|
||||
config.ExternalServices.PrometheusServiceURL = "http://prometheus.kubesphere-monitoring-system.svc:9090"
|
||||
|
||||
// ugly hack to get prometheus service url
|
||||
if pflag.Parsed() && pflag.Lookup("prometheus-endpoint") != nil {
|
||||
// Set prometheus
|
||||
endpoint, err := url.Parse(prometheus.PrometheusAPIEndpoint)
|
||||
if err != nil {
|
||||
config.ExternalServices.PrometheusServiceURL = endpoint.Path
|
||||
}
|
||||
}
|
||||
|
||||
config.ExternalServices.PrometheusCustomMetricsURL = config.ExternalServices.PrometheusServiceURL
|
||||
|
||||
// Set istio pilot discovery service url
|
||||
config.ExternalServices.Istio.UrlServiceVersion = s.IstioPilotServiceURL
|
||||
|
||||
kconfig.Set(config)
|
||||
}
|
||||
|
||||
func waitForResourceSync() {
|
||||
stopChan := signals.SetupSignalHandler()
|
||||
|
||||
@@ -96,6 +145,7 @@ func waitForResourceSync() {
|
||||
informerFactory.Apps().V1().StatefulSets().Lister()
|
||||
informerFactory.Apps().V1().Deployments().Lister()
|
||||
informerFactory.Apps().V1().DaemonSets().Lister()
|
||||
informerFactory.Apps().V1().ReplicaSets().Lister()
|
||||
|
||||
informerFactory.Batch().V1().Jobs().Lister()
|
||||
informerFactory.Batch().V1beta1().CronJobs().Lister()
|
||||
|
||||
27
cmd/ks-iam/app/options/options.go
Normal file
27
cmd/ks-iam/app/options/options.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
genericoptions "kubesphere.io/kubesphere/pkg/options"
|
||||
)
|
||||
|
||||
type ServerRunOptions struct {
|
||||
GenericServerRunOptions *genericoptions.ServerRunOptions
|
||||
AdminEmail string
|
||||
AdminPassword string
|
||||
TokenExpireTime string
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
s := &ServerRunOptions{
|
||||
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.AdminEmail, "admin-email", "admin@kubesphere.io", "default administrator's email")
|
||||
fs.StringVar(&s.AdminPassword, "admin-password", "passw0rd", "default administrator's password")
|
||||
fs.StringVar(&s.TokenExpireTime, "token-expire-time", "24h", "token expire time")
|
||||
s.GenericServerRunOptions.AddFlags(fs)
|
||||
}
|
||||
@@ -22,18 +22,20 @@ import (
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/cmd/ks-iam/app/options"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/filter"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/options"
|
||||
"kubesphere.io/kubesphere/pkg/signals"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewAPIServerCommand() *cobra.Command {
|
||||
s := options.SharedOptions
|
||||
s := options.NewServerRunOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ks-iam",
|
||||
@@ -44,17 +46,29 @@ cluster's shared state through which all other components interact.`,
|
||||
return Run(s)
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddFlagSet(s.CommandLine)
|
||||
s.AddFlags(cmd.Flags())
|
||||
cmd.Flags().AddGoFlagSet(goflag.CommandLine)
|
||||
glog.CopyStandardLogTo("INFO")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func Run(s *options.ServerRunOptions) error {
|
||||
|
||||
pflag.VisitAll(func(flag *pflag.Flag) {
|
||||
log.Printf("FLAG: --%s=%q", flag.Name, flag.Value)
|
||||
})
|
||||
|
||||
var err error
|
||||
|
||||
err = iam.DatabaseInit()
|
||||
|
||||
expireTime, err := time.ParseDuration(s.TokenExpireTime)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = iam.Init(s.AdminEmail, s.AdminPassword, expireTime)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -65,14 +79,12 @@ func Run(s *options.ServerRunOptions) error {
|
||||
container := runtime.Container
|
||||
container.Filter(filter.Logging)
|
||||
|
||||
log.Printf("Server listening on %d.", s.InsecurePort)
|
||||
|
||||
if s.InsecurePort != 0 {
|
||||
err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.BindAddress, s.InsecurePort), container)
|
||||
if s.GenericServerRunOptions.InsecurePort != 0 {
|
||||
err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort), container)
|
||||
}
|
||||
|
||||
if s.SecurePort != 0 && len(s.TlsCertFile) > 0 && len(s.TlsPrivateKey) > 0 {
|
||||
err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.BindAddress, s.SecurePort), s.TlsCertFile, s.TlsPrivateKey, container)
|
||||
if s.GenericServerRunOptions.SecurePort != 0 && len(s.GenericServerRunOptions.TlsCertFile) > 0 && len(s.GenericServerRunOptions.TlsPrivateKey) > 0 {
|
||||
err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort), s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey, container)
|
||||
}
|
||||
|
||||
return err
|
||||
|
||||
239
config/crds/app_v1beta1_application.yaml
Normal file
239
config/crds/app_v1beta1_application.yaml
Normal file
@@ -0,0 +1,239 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: applications.app.k8s.io
|
||||
spec:
|
||||
group: app.k8s.io
|
||||
names:
|
||||
kind: Application
|
||||
plural: applications
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
assemblyPhase:
|
||||
type: string
|
||||
componentKinds:
|
||||
items:
|
||||
type: object
|
||||
type: array
|
||||
descriptor:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
icons:
|
||||
items:
|
||||
properties:
|
||||
size:
|
||||
type: string
|
||||
src:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- src
|
||||
type: object
|
||||
type: array
|
||||
keywords:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
links:
|
||||
items:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
maintainers:
|
||||
items:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
notes:
|
||||
type: string
|
||||
owners:
|
||||
items:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
type:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
valueFrom:
|
||||
properties:
|
||||
configMapKeyRef:
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
fieldPath:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
resourceVersion:
|
||||
type: string
|
||||
uid:
|
||||
type: string
|
||||
type: object
|
||||
ingressRef:
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
fieldPath:
|
||||
type: string
|
||||
host:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
resourceVersion:
|
||||
type: string
|
||||
uid:
|
||||
type: string
|
||||
type: object
|
||||
secretKeyRef:
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
fieldPath:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
resourceVersion:
|
||||
type: string
|
||||
uid:
|
||||
type: string
|
||||
type: object
|
||||
serviceRef:
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
fieldPath:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
port:
|
||||
format: int32
|
||||
type: integer
|
||||
resourceVersion:
|
||||
type: string
|
||||
uid:
|
||||
type: string
|
||||
type: object
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
selector:
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
properties:
|
||||
components:
|
||||
items:
|
||||
properties:
|
||||
group:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
link:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
conditions:
|
||||
items:
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
format: date-time
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- status
|
||||
type: object
|
||||
type: array
|
||||
observedGeneration:
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
version: v1beta1
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
1116
config/crds/istio-crds.yaml
Normal file
1116
config/crds/istio-crds.yaml
Normal file
File diff suppressed because it is too large
Load Diff
763
config/crds/istio_v1alpha3_destinationrule.yaml
Normal file
763
config/crds/istio_v1alpha3_destinationrule.yaml
Normal file
@@ -0,0 +1,763 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: destinationrules.istio.kubesphere.io
|
||||
spec:
|
||||
group: istio.kubesphere.io
|
||||
names:
|
||||
kind: DestinationRule
|
||||
plural: destinationrules
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service from the service registry.
|
||||
Service names are looked up from the platform''s service registry
|
||||
(e.g., Kubernetes services, Consul services, etc.) and from the hosts
|
||||
declared by [ServiceEntries](#ServiceEntry). Rules defined for services
|
||||
that do not exist in the service registry will be ignored. *Note
|
||||
for Kubernetes users*: When short names are used (e.g. "reviews" instead
|
||||
of "reviews.default.svc.cluster.local"), Istio will interpret the
|
||||
short name based on the namespace of the rule, not the service. A
|
||||
rule in the "default" namespace containing a host "reviews will be
|
||||
interpreted as "reviews.default.svc.cluster.local", irrespective of
|
||||
the actual namespace associated with the reviews service. _To avoid
|
||||
potential misconfigurations, it is recommended to always use fully
|
||||
qualified domain names over short names._ Note that the host field
|
||||
applies to both HTTP and TCP services.'
|
||||
type: string
|
||||
subsets:
|
||||
description: One or more named sets that represent individual versions
|
||||
of a service. Traffic policies can be overridden at subset level.
|
||||
items:
|
||||
properties:
|
||||
labels:
|
||||
description: REQUIRED. Labels apply a filter over the endpoints
|
||||
of a service in the service registry. See route rules for examples
|
||||
of usage.
|
||||
type: object
|
||||
name:
|
||||
description: REQUIRED. Name of the subset. The service name and
|
||||
the subset name can be used for traffic splitting in a route
|
||||
rule.
|
||||
type: string
|
||||
trafficPolicy:
|
||||
description: Traffic policies that apply to this subset. Subsets
|
||||
inherit the traffic policies specified at the DestinationRule
|
||||
level. Settings specified at the subset level will override
|
||||
the corresponding settings specified at the DestinationRule
|
||||
level.
|
||||
properties:
|
||||
connectionPool:
|
||||
description: Settings controlling the volume of connections
|
||||
to an upstream service
|
||||
properties:
|
||||
http:
|
||||
description: HTTP connection pool settings.
|
||||
properties:
|
||||
maxRequestsPerConnection:
|
||||
description: Maximum number of requests per connection
|
||||
to a backend. Setting this parameter to 1 disables
|
||||
keep alive.
|
||||
format: int32
|
||||
type: integer
|
||||
maxRetries:
|
||||
description: Maximum number of retries that can be
|
||||
outstanding to all hosts in a cluster at a given
|
||||
time. Defaults to 3.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
tcp:
|
||||
description: Settings common to both HTTP and TCP upstream
|
||||
connections.
|
||||
properties:
|
||||
connectTimeout:
|
||||
description: TCP connection timeout.
|
||||
type: string
|
||||
maxConnections:
|
||||
description: Maximum number of HTTP1 /TCP connections
|
||||
to a destination host.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
loadBalancer:
|
||||
description: Settings controlling the load balancer algorithms.
|
||||
properties:
|
||||
consistentHash:
|
||||
properties:
|
||||
httpCookie:
|
||||
description: Hash based on HTTP cookie.
|
||||
properties:
|
||||
name:
|
||||
description: REQUIRED. Name of the cookie.
|
||||
type: string
|
||||
path:
|
||||
description: Path to set for the cookie.
|
||||
type: string
|
||||
ttl:
|
||||
description: REQUIRED. Lifetime of the cookie.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- ttl
|
||||
type: object
|
||||
httpHeaderName:
|
||||
description: 'It is required to specify exactly one
|
||||
of the fields as hash key: HttpHeaderName, HttpCookie,
|
||||
or UseSourceIP. Hash based on a specific HTTP header.'
|
||||
type: string
|
||||
minimumRingSize:
|
||||
description: The minimum number of virtual nodes to
|
||||
use for the hash ring. Defaults to 1024. Larger
|
||||
ring sizes result in more granular load distributions.
|
||||
If the number of hosts in the load balancing pool
|
||||
is larger than the ring size, each host will be
|
||||
assigned a single virtual node.
|
||||
format: int64
|
||||
type: integer
|
||||
useSourceIp:
|
||||
description: Hash based on the source IP address.
|
||||
type: boolean
|
||||
type: object
|
||||
simple:
|
||||
description: 'It is required to specify exactly one of
|
||||
the fields: Simple or ConsistentHash'
|
||||
type: string
|
||||
type: object
|
||||
outlierDetection:
|
||||
description: Settings controlling eviction of unhealthy hosts
|
||||
from the load balancing pool
|
||||
properties:
|
||||
baseEjectionTime:
|
||||
description: 'Minimum ejection duration. A host will remain
|
||||
ejected for a period equal to the product of minimum
|
||||
ejection duration and the number of times the host has
|
||||
been ejected. This technique allows the system to automatically
|
||||
increase the ejection period for unhealthy upstream
|
||||
servers. format: 1h/1m/1s/1ms. MUST BE >=1ms. Default
|
||||
is 30s.'
|
||||
type: string
|
||||
consecutiveErrors:
|
||||
description: Number of errors before a host is ejected
|
||||
from the connection pool. Defaults to 5. When the upstream
|
||||
host is accessed over HTTP, a 5xx return code qualifies
|
||||
as an error. When the upstream host is accessed over
|
||||
an opaque TCP connection, connect timeouts and connection
|
||||
error/failure events qualify as an error.
|
||||
format: int32
|
||||
type: integer
|
||||
interval:
|
||||
description: 'Time interval between ejection sweep analysis.
|
||||
format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s.'
|
||||
type: string
|
||||
maxEjectionPercent:
|
||||
description: Maximum % of hosts in the load balancing
|
||||
pool for the upstream service that can be ejected. Defaults
|
||||
to 10%.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
portLevelSettings:
|
||||
description: Traffic policies specific to individual ports.
|
||||
Note that port level settings will override the destination-level
|
||||
settings. Traffic settings specified at the destination-level
|
||||
will not be inherited when overridden by port-level settings,
|
||||
i.e. default values will be applied to fields omitted in
|
||||
port-level traffic policies.
|
||||
items:
|
||||
properties:
|
||||
connectionPool:
|
||||
description: Settings controlling the volume of connections
|
||||
to an upstream service
|
||||
properties:
|
||||
http:
|
||||
description: HTTP connection pool settings.
|
||||
properties:
|
||||
maxRequestsPerConnection:
|
||||
description: Maximum number of requests per
|
||||
connection to a backend. Setting this parameter
|
||||
to 1 disables keep alive.
|
||||
format: int32
|
||||
type: integer
|
||||
maxRetries:
|
||||
description: Maximum number of retries that
|
||||
can be outstanding to all hosts in a cluster
|
||||
at a given time. Defaults to 3.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
tcp:
|
||||
description: Settings common to both HTTP and TCP
|
||||
upstream connections.
|
||||
properties:
|
||||
connectTimeout:
|
||||
description: TCP connection timeout.
|
||||
type: string
|
||||
maxConnections:
|
||||
description: Maximum number of HTTP1 /TCP connections
|
||||
to a destination host.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
loadBalancer:
|
||||
description: Settings controlling the load balancer
|
||||
algorithms.
|
||||
properties:
|
||||
consistentHash:
|
||||
properties:
|
||||
httpCookie:
|
||||
description: Hash based on HTTP cookie.
|
||||
properties:
|
||||
name:
|
||||
description: REQUIRED. Name of the cookie.
|
||||
type: string
|
||||
path:
|
||||
description: Path to set for the cookie.
|
||||
type: string
|
||||
ttl:
|
||||
description: REQUIRED. Lifetime of the cookie.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- ttl
|
||||
type: object
|
||||
httpHeaderName:
|
||||
description: 'It is required to specify exactly
|
||||
one of the fields as hash key: HttpHeaderName,
|
||||
HttpCookie, or UseSourceIP. Hash based on
|
||||
a specific HTTP header.'
|
||||
type: string
|
||||
minimumRingSize:
|
||||
description: The minimum number of virtual nodes
|
||||
to use for the hash ring. Defaults to 1024.
|
||||
Larger ring sizes result in more granular
|
||||
load distributions. If the number of hosts
|
||||
in the load balancing pool is larger than
|
||||
the ring size, each host will be assigned
|
||||
a single virtual node.
|
||||
format: int64
|
||||
type: integer
|
||||
useSourceIp:
|
||||
description: Hash based on the source IP address.
|
||||
type: boolean
|
||||
type: object
|
||||
simple:
|
||||
description: 'It is required to specify exactly
|
||||
one of the fields: Simple or ConsistentHash'
|
||||
type: string
|
||||
type: object
|
||||
outlierDetection:
|
||||
description: Settings controlling eviction of unhealthy
|
||||
hosts from the load balancing pool
|
||||
properties:
|
||||
baseEjectionTime:
|
||||
description: 'Minimum ejection duration. A host
|
||||
will remain ejected for a period equal to the
|
||||
product of minimum ejection duration and the number
|
||||
of times the host has been ejected. This technique
|
||||
allows the system to automatically increase the
|
||||
ejection period for unhealthy upstream servers.
|
||||
format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is
|
||||
30s.'
|
||||
type: string
|
||||
consecutiveErrors:
|
||||
description: Number of errors before a host is ejected
|
||||
from the connection pool. Defaults to 5. When
|
||||
the upstream host is accessed over HTTP, a 5xx
|
||||
return code qualifies as an error. When the upstream
|
||||
host is accessed over an opaque TCP connection,
|
||||
connect timeouts and connection error/failure
|
||||
events qualify as an error.
|
||||
format: int32
|
||||
type: integer
|
||||
interval:
|
||||
description: 'Time interval between ejection sweep
|
||||
analysis. format: 1h/1m/1s/1ms. MUST BE >=1ms.
|
||||
Default is 10s.'
|
||||
type: string
|
||||
maxEjectionPercent:
|
||||
description: Maximum % of hosts in the load balancing
|
||||
pool for the upstream service that can be ejected.
|
||||
Defaults to 10%.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
port:
|
||||
description: Specifies the port name or number of a
|
||||
port on the destination service on which this policy
|
||||
is being applied. Names must comply with DNS label
|
||||
syntax (rfc1035) and therefore cannot collide with
|
||||
numbers. If there are multiple ports on a service
|
||||
with the same protocol the names should be of the
|
||||
form <protocol-name>-<DNS label>.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
tls:
|
||||
description: TLS related settings for connections to
|
||||
the upstream service.
|
||||
properties:
|
||||
caCertificates:
|
||||
description: 'OPTIONAL: The path to the file containing
|
||||
certificate authority certificates to use in verifying
|
||||
a presented server certificate. If omitted, the
|
||||
proxy will not verify the server''s certificate.
|
||||
Should be empty if mode is `ISTIO_MUTUAL`.'
|
||||
type: string
|
||||
clientCertificate:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path
|
||||
to the file holding the client-side TLS certificate
|
||||
to use. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
mode:
|
||||
description: 'REQUIRED: Indicates whether connections
|
||||
to this port should be secured using TLS. The
|
||||
value of this field determines how TLS is enforced.'
|
||||
type: string
|
||||
privateKey:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path
|
||||
to the file holding the client's private key.
|
||||
Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
sni:
|
||||
description: SNI string to present to the server
|
||||
during TLS handshake. Should be empty if mode
|
||||
is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
subjectAltNames:
|
||||
description: A list of alternate names to verify
|
||||
the subject identity in the certificate. If specified,
|
||||
the proxy will verify that the server certificate's
|
||||
subject alt name matches one of the specified
|
||||
values. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- mode
|
||||
type: object
|
||||
required:
|
||||
- port
|
||||
type: object
|
||||
type: array
|
||||
tls:
|
||||
description: TLS related settings for connections to the upstream
|
||||
service.
|
||||
properties:
|
||||
caCertificates:
|
||||
description: 'OPTIONAL: The path to the file containing
|
||||
certificate authority certificates to use in verifying
|
||||
a presented server certificate. If omitted, the proxy
|
||||
will not verify the server''s certificate. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.'
|
||||
type: string
|
||||
clientCertificate:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path to
|
||||
the file holding the client-side TLS certificate to
|
||||
use. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
mode:
|
||||
description: 'REQUIRED: Indicates whether connections
|
||||
to this port should be secured using TLS. The value
|
||||
of this field determines how TLS is enforced.'
|
||||
type: string
|
||||
privateKey:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path to
|
||||
the file holding the client's private key. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
sni:
|
||||
description: SNI string to present to the server during
|
||||
TLS handshake. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
subjectAltNames:
|
||||
description: A list of alternate names to verify the subject
|
||||
identity in the certificate. If specified, the proxy
|
||||
will verify that the server certificate's subject alt
|
||||
name matches one of the specified values. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- mode
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- labels
|
||||
type: object
|
||||
type: array
|
||||
trafficPolicy:
|
||||
description: Traffic policies to apply (load balancing policy, connection
|
||||
pool sizes, outlier detection).
|
||||
properties:
|
||||
connectionPool:
|
||||
description: Settings controlling the volume of connections to an
|
||||
upstream service
|
||||
properties:
|
||||
http:
|
||||
description: HTTP connection pool settings.
|
||||
properties:
|
||||
maxRequestsPerConnection:
|
||||
description: Maximum number of requests per connection to
|
||||
a backend. Setting this parameter to 1 disables keep alive.
|
||||
format: int32
|
||||
type: integer
|
||||
maxRetries:
|
||||
description: Maximum number of retries that can be outstanding
|
||||
to all hosts in a cluster at a given time. Defaults to
|
||||
3.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
tcp:
|
||||
description: Settings common to both HTTP and TCP upstream connections.
|
||||
properties:
|
||||
connectTimeout:
|
||||
description: TCP connection timeout.
|
||||
type: string
|
||||
maxConnections:
|
||||
description: Maximum number of HTTP1 /TCP connections to
|
||||
a destination host.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
loadBalancer:
|
||||
description: Settings controlling the load balancer algorithms.
|
||||
properties:
|
||||
consistentHash:
|
||||
properties:
|
||||
httpCookie:
|
||||
description: Hash based on HTTP cookie.
|
||||
properties:
|
||||
name:
|
||||
description: REQUIRED. Name of the cookie.
|
||||
type: string
|
||||
path:
|
||||
description: Path to set for the cookie.
|
||||
type: string
|
||||
ttl:
|
||||
description: REQUIRED. Lifetime of the cookie.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- ttl
|
||||
type: object
|
||||
httpHeaderName:
|
||||
description: 'It is required to specify exactly one of the
|
||||
fields as hash key: HttpHeaderName, HttpCookie, or UseSourceIP.
|
||||
Hash based on a specific HTTP header.'
|
||||
type: string
|
||||
minimumRingSize:
|
||||
description: The minimum number of virtual nodes to use
|
||||
for the hash ring. Defaults to 1024. Larger ring sizes
|
||||
result in more granular load distributions. If the number
|
||||
of hosts in the load balancing pool is larger than the
|
||||
ring size, each host will be assigned a single virtual
|
||||
node.
|
||||
format: int64
|
||||
type: integer
|
||||
useSourceIp:
|
||||
description: Hash based on the source IP address.
|
||||
type: boolean
|
||||
type: object
|
||||
simple:
|
||||
description: 'It is required to specify exactly one of the fields:
|
||||
Simple or ConsistentHash'
|
||||
type: string
|
||||
type: object
|
||||
outlierDetection:
|
||||
description: Settings controlling eviction of unhealthy hosts from
|
||||
the load balancing pool
|
||||
properties:
|
||||
baseEjectionTime:
|
||||
description: 'Minimum ejection duration. A host will remain
|
||||
ejected for a period equal to the product of minimum ejection
|
||||
duration and the number of times the host has been ejected.
|
||||
This technique allows the system to automatically increase
|
||||
the ejection period for unhealthy upstream servers. format:
|
||||
1h/1m/1s/1ms. MUST BE >=1ms. Default is 30s.'
|
||||
type: string
|
||||
consecutiveErrors:
|
||||
description: Number of errors before a host is ejected from
|
||||
the connection pool. Defaults to 5. When the upstream host
|
||||
is accessed over HTTP, a 5xx return code qualifies as an error.
|
||||
When the upstream host is accessed over an opaque TCP connection,
|
||||
connect timeouts and connection error/failure events qualify
|
||||
as an error.
|
||||
format: int32
|
||||
type: integer
|
||||
interval:
|
||||
description: 'Time interval between ejection sweep analysis.
|
||||
format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s.'
|
||||
type: string
|
||||
maxEjectionPercent:
|
||||
description: Maximum % of hosts in the load balancing pool for
|
||||
the upstream service that can be ejected. Defaults to 10%.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
portLevelSettings:
|
||||
description: Traffic policies specific to individual ports. Note
|
||||
that port level settings will override the destination-level settings.
|
||||
Traffic settings specified at the destination-level will not be
|
||||
inherited when overridden by port-level settings, i.e. default
|
||||
values will be applied to fields omitted in port-level traffic
|
||||
policies.
|
||||
items:
|
||||
properties:
|
||||
connectionPool:
|
||||
description: Settings controlling the volume of connections
|
||||
to an upstream service
|
||||
properties:
|
||||
http:
|
||||
description: HTTP connection pool settings.
|
||||
properties:
|
||||
maxRequestsPerConnection:
|
||||
description: Maximum number of requests per connection
|
||||
to a backend. Setting this parameter to 1 disables
|
||||
keep alive.
|
||||
format: int32
|
||||
type: integer
|
||||
maxRetries:
|
||||
description: Maximum number of retries that can be
|
||||
outstanding to all hosts in a cluster at a given
|
||||
time. Defaults to 3.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
tcp:
|
||||
description: Settings common to both HTTP and TCP upstream
|
||||
connections.
|
||||
properties:
|
||||
connectTimeout:
|
||||
description: TCP connection timeout.
|
||||
type: string
|
||||
maxConnections:
|
||||
description: Maximum number of HTTP1 /TCP connections
|
||||
to a destination host.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
loadBalancer:
|
||||
description: Settings controlling the load balancer algorithms.
|
||||
properties:
|
||||
consistentHash:
|
||||
properties:
|
||||
httpCookie:
|
||||
description: Hash based on HTTP cookie.
|
||||
properties:
|
||||
name:
|
||||
description: REQUIRED. Name of the cookie.
|
||||
type: string
|
||||
path:
|
||||
description: Path to set for the cookie.
|
||||
type: string
|
||||
ttl:
|
||||
description: REQUIRED. Lifetime of the cookie.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- ttl
|
||||
type: object
|
||||
httpHeaderName:
|
||||
description: 'It is required to specify exactly one
|
||||
of the fields as hash key: HttpHeaderName, HttpCookie,
|
||||
or UseSourceIP. Hash based on a specific HTTP header.'
|
||||
type: string
|
||||
minimumRingSize:
|
||||
description: The minimum number of virtual nodes to
|
||||
use for the hash ring. Defaults to 1024. Larger
|
||||
ring sizes result in more granular load distributions.
|
||||
If the number of hosts in the load balancing pool
|
||||
is larger than the ring size, each host will be
|
||||
assigned a single virtual node.
|
||||
format: int64
|
||||
type: integer
|
||||
useSourceIp:
|
||||
description: Hash based on the source IP address.
|
||||
type: boolean
|
||||
type: object
|
||||
simple:
|
||||
description: 'It is required to specify exactly one of
|
||||
the fields: Simple or ConsistentHash'
|
||||
type: string
|
||||
type: object
|
||||
outlierDetection:
|
||||
description: Settings controlling eviction of unhealthy hosts
|
||||
from the load balancing pool
|
||||
properties:
|
||||
baseEjectionTime:
|
||||
description: 'Minimum ejection duration. A host will remain
|
||||
ejected for a period equal to the product of minimum
|
||||
ejection duration and the number of times the host has
|
||||
been ejected. This technique allows the system to automatically
|
||||
increase the ejection period for unhealthy upstream
|
||||
servers. format: 1h/1m/1s/1ms. MUST BE >=1ms. Default
|
||||
is 30s.'
|
||||
type: string
|
||||
consecutiveErrors:
|
||||
description: Number of errors before a host is ejected
|
||||
from the connection pool. Defaults to 5. When the upstream
|
||||
host is accessed over HTTP, a 5xx return code qualifies
|
||||
as an error. When the upstream host is accessed over
|
||||
an opaque TCP connection, connect timeouts and connection
|
||||
error/failure events qualify as an error.
|
||||
format: int32
|
||||
type: integer
|
||||
interval:
|
||||
description: 'Time interval between ejection sweep analysis.
|
||||
format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s.'
|
||||
type: string
|
||||
maxEjectionPercent:
|
||||
description: Maximum % of hosts in the load balancing
|
||||
pool for the upstream service that can be ejected. Defaults
|
||||
to 10%.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
port:
|
||||
description: Specifies the port name or number of a port on
|
||||
the destination service on which this policy is being applied. Names
|
||||
must comply with DNS label syntax (rfc1035) and therefore
|
||||
cannot collide with numbers. If there are multiple ports
|
||||
on a service with the same protocol the names should be
|
||||
of the form <protocol-name>-<DNS label>.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
tls:
|
||||
description: TLS related settings for connections to the upstream
|
||||
service.
|
||||
properties:
|
||||
caCertificates:
|
||||
description: 'OPTIONAL: The path to the file containing
|
||||
certificate authority certificates to use in verifying
|
||||
a presented server certificate. If omitted, the proxy
|
||||
will not verify the server''s certificate. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.'
|
||||
type: string
|
||||
clientCertificate:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path to
|
||||
the file holding the client-side TLS certificate to
|
||||
use. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
mode:
|
||||
description: 'REQUIRED: Indicates whether connections
|
||||
to this port should be secured using TLS. The value
|
||||
of this field determines how TLS is enforced.'
|
||||
type: string
|
||||
privateKey:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path to
|
||||
the file holding the client's private key. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
sni:
|
||||
description: SNI string to present to the server during
|
||||
TLS handshake. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
subjectAltNames:
|
||||
description: A list of alternate names to verify the subject
|
||||
identity in the certificate. If specified, the proxy
|
||||
will verify that the server certificate's subject alt
|
||||
name matches one of the specified values. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- mode
|
||||
type: object
|
||||
required:
|
||||
- port
|
||||
type: object
|
||||
type: array
|
||||
tls:
|
||||
description: TLS related settings for connections to the upstream
|
||||
service.
|
||||
properties:
|
||||
caCertificates:
|
||||
description: 'OPTIONAL: The path to the file containing certificate
|
||||
authority certificates to use in verifying a presented server
|
||||
certificate. If omitted, the proxy will not verify the server''s
|
||||
certificate. Should be empty if mode is `ISTIO_MUTUAL`.'
|
||||
type: string
|
||||
clientCertificate:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path to the file
|
||||
holding the client-side TLS certificate to use. Should be
|
||||
empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
mode:
|
||||
description: 'REQUIRED: Indicates whether connections to this
|
||||
port should be secured using TLS. The value of this field
|
||||
determines how TLS is enforced.'
|
||||
type: string
|
||||
privateKey:
|
||||
description: REQUIRED if mode is `MUTUAL`. The path to the file
|
||||
holding the client's private key. Should be empty if mode
|
||||
is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
sni:
|
||||
description: SNI string to present to the server during TLS
|
||||
handshake. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
type: string
|
||||
subjectAltNames:
|
||||
description: A list of alternate names to verify the subject
|
||||
identity in the certificate. If specified, the proxy will
|
||||
verify that the server certificate's subject alt name matches
|
||||
one of the specified values. Should be empty if mode is `ISTIO_MUTUAL`.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- mode
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
version: v1alpha3
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
129
config/crds/istio_v1alpha3_gateway.yaml
Normal file
129
config/crds/istio_v1alpha3_gateway.yaml
Normal file
@@ -0,0 +1,129 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: gateways.istio.kubesphere.io
|
||||
spec:
|
||||
group: istio.kubesphere.io
|
||||
names:
|
||||
kind: Gateway
|
||||
plural: gateways
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
selector:
|
||||
description: One or more labels that indicate a specific set of pods/VMs
|
||||
on which this gateway configuration should be applied. If no selectors
|
||||
are provided, the gateway will be implemented by the default istio-ingress
|
||||
controller.
|
||||
type: object
|
||||
servers:
|
||||
description: 'REQUIRED: A list of server specifications.'
|
||||
items:
|
||||
properties:
|
||||
hosts:
|
||||
description: A list of hosts exposed by this gateway. While typically
|
||||
applicable to HTTP services, it can also be used for TCP services
|
||||
using TLS with SNI. Standard DNS wildcard prefix syntax is permitted. A
|
||||
VirtualService that is bound to a gateway must having a matching
|
||||
host in its default destination. Specifically one of the VirtualService
|
||||
destination hosts is a strict suffix of a gateway host or a
|
||||
gateway host is a suffix of one of the VirtualService hosts.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
port:
|
||||
description: 'REQUIRED: The Port on which the proxy should listen
|
||||
for incoming connections'
|
||||
properties:
|
||||
name:
|
||||
description: Label assigned to the port.
|
||||
type: string
|
||||
number:
|
||||
description: 'REQUIRED: A valid non-negative integer port
|
||||
number.'
|
||||
format: int64
|
||||
type: integer
|
||||
protocol:
|
||||
description: 'REQUIRED: The protocol exposed on the port.
|
||||
MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP.'
|
||||
type: string
|
||||
required:
|
||||
- number
|
||||
- protocol
|
||||
type: object
|
||||
tls:
|
||||
description: Set of TLS related options that govern the server's
|
||||
behavior. Use these options to control if all http requests
|
||||
should be redirected to https, and the TLS modes to use.
|
||||
properties:
|
||||
caCertificates:
|
||||
description: REQUIRED if mode is "MUTUAL". The path to a file
|
||||
containing certificate authority certificates to use in
|
||||
verifying a presented client side certificate.
|
||||
type: string
|
||||
httpsRedirect:
|
||||
description: If set to true, the load balancer will send a
|
||||
302 redirect for all http connections, asking the clients
|
||||
to use HTTPS.
|
||||
type: boolean
|
||||
mode:
|
||||
description: 'Optional: Indicates whether connections to this
|
||||
port should be secured using TLS. The value of this field
|
||||
determines how TLS is enforced.'
|
||||
type: string
|
||||
privateKey:
|
||||
description: REQUIRED if mode is "SIMPLE" or "MUTUAL". The
|
||||
path to the file holding the server's private key.
|
||||
type: string
|
||||
serverCertificate:
|
||||
description: REQUIRED if mode is "SIMPLE" or "MUTUAL". The
|
||||
path to the file holding the server-side TLS certificate
|
||||
to use.
|
||||
type: string
|
||||
subjectAltNames:
|
||||
description: A list of alternate names to verify the subject
|
||||
identity in the certificate presented by the client.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- httpsRedirect
|
||||
- serverCertificate
|
||||
- privateKey
|
||||
- caCertificates
|
||||
- subjectAltNames
|
||||
type: object
|
||||
required:
|
||||
- port
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- servers
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
version: v1alpha3
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
695
config/crds/istio_v1alpha3_virtualservice.yaml
Normal file
695
config/crds/istio_v1alpha3_virtualservice.yaml
Normal file
@@ -0,0 +1,695 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: virtualservices.istio.kubesphere.io
|
||||
spec:
|
||||
group: istio.kubesphere.io
|
||||
names:
|
||||
kind: VirtualService
|
||||
plural: virtualservices
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
gateways:
|
||||
description: The names of gateways and sidecars that should apply these
|
||||
routes. A single VirtualService is used for sidecars inside the mesh
|
||||
as well as for one or more gateways. The selection condition imposed
|
||||
by this field can be overridden using the source field in the match
|
||||
conditions of HTTP/TCP routes. The reserved word "mesh" is used to
|
||||
imply all the sidecars in the mesh. When this field is omitted, the
|
||||
default gateway ("mesh") will be used, which would apply the rule
|
||||
to all sidecars in the mesh. If a list of gateway names is provided,
|
||||
the rules will apply only to the gateways. To apply the rules to both
|
||||
gateways and sidecars, specify "mesh" as one of the gateway names.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
hosts:
|
||||
description: REQUIRED. The destination address for traffic captured
|
||||
by this virtual service. Could be a DNS name with wildcard prefix
|
||||
or a CIDR prefix. Depending on the platform, short-names can also
|
||||
be used instead of a FQDN (i.e. has no dots in the name). In such
|
||||
a scenario, the FQDN of the host would be derived based on the underlying
|
||||
platform. For example on Kubernetes, when hosts contains a short
|
||||
name, Istio will interpret the short name based on the namespace of
|
||||
the rule. Thus, when a client namespace applies a rule in the "default"
|
||||
namespace containing a name "reviews, Istio will setup routes to the
|
||||
"reviews.default.svc.cluster.local" service. However, if a different
|
||||
name such as "reviews.sales.svc.cluster.local" is used, it would be
|
||||
treated as a FQDN during virtual host matching. In Consul, a plain
|
||||
service name would be resolved to the FQDN "reviews.service.consul". Note
|
||||
that the hosts field applies to both HTTP and TCP services. Service
|
||||
inside the mesh, i.e., those found in the service registry, must always
|
||||
be referred to using their alphanumeric names. IP addresses or CIDR
|
||||
prefixes are allowed only for services defined via the Gateway.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
http:
|
||||
description: An ordered list of route rules for HTTP traffic. The first
|
||||
rule matching an incoming request is used.
|
||||
items:
|
||||
properties:
|
||||
appendHeaders:
|
||||
description: Additional HTTP headers to add before forwarding
|
||||
a request to the destination service.
|
||||
type: object
|
||||
corsPolicy:
|
||||
description: Cross-Origin Resource Sharing policy
|
||||
properties:
|
||||
allowCredentials:
|
||||
description: Indicates whether the caller is allowed to send
|
||||
the actual request (not the preflight) using credentials.
|
||||
Translates to Access-Control-Allow-Credentials header.
|
||||
type: boolean
|
||||
allowHeaders:
|
||||
description: List of HTTP headers that can be used when requesting
|
||||
the resource. Serialized to Access-Control-Allow-Methods
|
||||
header.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowMethods:
|
||||
description: List of HTTP methods allowed to access the resource.
|
||||
The content will be serialized into the Access-Control-Allow-Methods
|
||||
header.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowOrigin:
|
||||
description: The list of origins that are allowed to perform
|
||||
CORS requests. The content will be serialized into the Access-Control-Allow-Origin
|
||||
header. Wildcard * will allow all origins.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
exposeHeaders:
|
||||
description: A white list of HTTP headers that the browsers
|
||||
are allowed to access. Serialized into Access-Control-Expose-Headers
|
||||
header.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
maxAge:
|
||||
description: Specifies how long the the results of a preflight
|
||||
request can be cached. Translates to the Access-Control-Max-Age
|
||||
header.
|
||||
type: string
|
||||
type: object
|
||||
fault:
|
||||
description: Fault injection policy to apply on HTTP traffic.
|
||||
properties:
|
||||
abort:
|
||||
description: Abort Http request attempts and return error
|
||||
codes back to downstream service, giving the impression
|
||||
that the upstream service is faulty.
|
||||
properties:
|
||||
httpStatus:
|
||||
description: REQUIRED. HTTP status code to use to abort
|
||||
the Http request.
|
||||
format: int64
|
||||
type: integer
|
||||
percent:
|
||||
description: Percentage of requests to be aborted with
|
||||
the error code provided (0-100).
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- httpStatus
|
||||
type: object
|
||||
delay:
|
||||
description: Delay requests before forwarding, emulating various
|
||||
failures such as network issues, overloaded upstream service,
|
||||
etc.
|
||||
properties:
|
||||
exponentialDelay:
|
||||
description: (-- Add a delay (based on an exponential
|
||||
function) before forwarding the request. mean delay
|
||||
needed to derive the exponential delay values --)
|
||||
type: string
|
||||
fixedDelay:
|
||||
description: 'REQUIRED. Add a fixed delay before forwarding
|
||||
the request. Format: 1h/1m/1s/1ms. MUST be >=1ms.'
|
||||
type: string
|
||||
percent:
|
||||
description: Percentage of requests on which the delay
|
||||
will be injected (0-100).
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- fixedDelay
|
||||
type: object
|
||||
type: object
|
||||
match:
|
||||
description: Match conditions to be satisfied for the rule to
|
||||
be activated. All conditions inside a single match block have
|
||||
AND semantics, while the list of match blocks have OR semantics.
|
||||
The rule is matched if any one of the match blocks succeed.
|
||||
items:
|
||||
properties:
|
||||
authority:
|
||||
description: 'HTTP Authority values are case-sensitive and
|
||||
formatted as follows: - `exact: "value"` for exact string
|
||||
match - `prefix: "value"` for prefix-based match - `regex:
|
||||
"value"` for ECMAscript style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
gateways:
|
||||
description: Names of gateways where the rule should be
|
||||
applied to. Gateway names at the top of the VirtualService
|
||||
(if any) are overridden. The gateway match is independent
|
||||
of sourceLabels.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
headers:
|
||||
description: 'The header keys must be lowercase and use
|
||||
hyphen as the separator, e.g. _x-request-id_. Header
|
||||
values are case-sensitive and formatted as follows: -
|
||||
`exact: "value"` for exact string match - `prefix: "value"`
|
||||
for prefix-based match - `regex: "value"` for ECMAscript
|
||||
style regex-based match **Note:** The keys `uri`, `scheme`,
|
||||
`method`, and `authority` will be ignored.'
|
||||
type: object
|
||||
method:
|
||||
description: 'HTTP Method values are case-sensitive and
|
||||
formatted as follows: - `exact: "value"` for exact string
|
||||
match - `prefix: "value"` for prefix-based match - `regex:
|
||||
"value"` for ECMAscript style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
port:
|
||||
description: Specifies the ports on the host that is being
|
||||
addressed. Many services only expose a single port or
|
||||
label ports with the protocols they support, in these
|
||||
cases it is not required to explicitly select the port.
|
||||
format: int32
|
||||
type: integer
|
||||
scheme:
|
||||
description: 'URI Scheme values are case-sensitive and formatted
|
||||
as follows: - `exact: "value"` for exact string match -
|
||||
`prefix: "value"` for prefix-based match - `regex: "value"`
|
||||
for ECMAscript style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
sourceLabels:
|
||||
description: One or more labels that constrain the applicability
|
||||
of a rule to workloads with the given labels. If the VirtualService
|
||||
has a list of gateways specified at the top, it should
|
||||
include the reserved gateway `mesh` in order for this
|
||||
field to be applicable.
|
||||
type: object
|
||||
uri:
|
||||
description: 'URI to match values are case-sensitive and
|
||||
formatted as follows: - `exact: "value"` for exact string
|
||||
match - `prefix: "value"` for prefix-based match - `regex:
|
||||
"value"` for ECMAscript style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
mirror:
|
||||
description: Mirror HTTP traffic to a another destination in addition
|
||||
to forwarding the requests to the intended destination. Mirrored
|
||||
traffic is on a best effort basis where the sidecar/gateway
|
||||
will not wait for the mirrored cluster to respond before returning
|
||||
the response from the original destination. Statistics will
|
||||
be generated for the mirrored destination.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service from the service
|
||||
registry. Service names are looked up from the platform''s
|
||||
service registry (e.g., Kubernetes services, Consul services,
|
||||
etc.) and from the hosts declared by [ServiceEntry](#ServiceEntry).
|
||||
Traffic forwarded to destinations that are not found in
|
||||
either of the two, will be dropped. *Note for Kubernetes
|
||||
users*: When short names are used (e.g. "reviews" instead
|
||||
of "reviews.default.svc.cluster.local"), Istio will interpret
|
||||
the short name based on the namespace of the rule, not the
|
||||
service. A rule in the "default" namespace containing a
|
||||
host "reviews will be interpreted as "reviews.default.svc.cluster.local",
|
||||
irrespective of the actual namespace associated with the
|
||||
reviews service. _To avoid potential misconfigurations,
|
||||
it is recommended to always use fully qualified domain names
|
||||
over short names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host that is being
|
||||
addressed. If a service exposes only a single port it is
|
||||
not required to explicitly select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the service. Applicable
|
||||
only to services within the mesh. The subset must be defined
|
||||
in a corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
redirect:
|
||||
description: A http rule can either redirect or forward (default)
|
||||
traffic. If traffic passthrough option is specified in the rule,
|
||||
route/redirect will be ignored. The redirect primitive can be
|
||||
used to send a HTTP 302 redirect to a different URI or Authority.
|
||||
properties:
|
||||
authority:
|
||||
description: On a redirect, overwrite the Authority/Host portion
|
||||
of the URL with this value.
|
||||
type: string
|
||||
uri:
|
||||
description: On a redirect, overwrite the Path portion of
|
||||
the URL with this value. Note that the entire path will
|
||||
be replaced, irrespective of the request URI being matched
|
||||
as an exact path or prefix.
|
||||
type: string
|
||||
type: object
|
||||
removeResponseHeaders:
|
||||
description: Http headers to remove before returning the response
|
||||
to the caller
|
||||
type: object
|
||||
retries:
|
||||
description: Retry policy for HTTP requests.
|
||||
properties:
|
||||
attempts:
|
||||
description: REQUIRED. Number of retries for a given request.
|
||||
The interval between retries will be determined automatically
|
||||
(25ms+). Actual number of retries attempted depends on the
|
||||
httpReqTimeout.
|
||||
format: int64
|
||||
type: integer
|
||||
perTryTimeout:
|
||||
description: 'Timeout per retry attempt for a given request.
|
||||
format: 1h/1m/1s/1ms. MUST BE >=1ms.'
|
||||
type: string
|
||||
required:
|
||||
- attempts
|
||||
- perTryTimeout
|
||||
type: object
|
||||
rewrite:
|
||||
description: Rewrite HTTP URIs and Authority headers. Rewrite
|
||||
cannot be used with Redirect primitive. Rewrite will be performed
|
||||
before forwarding.
|
||||
properties:
|
||||
authority:
|
||||
description: rewrite the Authority/Host header with this value.
|
||||
type: string
|
||||
uri:
|
||||
description: rewrite the path (or the prefix) portion of the
|
||||
URI with this value. If the original URI was matched based
|
||||
on prefix, the value provided in this field will replace
|
||||
the corresponding matched prefix.
|
||||
type: string
|
||||
type: object
|
||||
route:
|
||||
description: A http rule can either redirect or forward (default)
|
||||
traffic. The forwarding target can be one of several versions
|
||||
of a service (see glossary in beginning of document). Weights
|
||||
associated with the service version determine the proportion
|
||||
of traffic it receives.
|
||||
items:
|
||||
properties:
|
||||
destination:
|
||||
description: REQUIRED. Destination uniquely identifies the
|
||||
instances of a service to which the request/connection
|
||||
should be forwarded to.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service from the
|
||||
service registry. Service names are looked up from
|
||||
the platform''s service registry (e.g., Kubernetes
|
||||
services, Consul services, etc.) and from the hosts
|
||||
declared by [ServiceEntry](#ServiceEntry). Traffic
|
||||
forwarded to destinations that are not found in either
|
||||
of the two, will be dropped. *Note for Kubernetes
|
||||
users*: When short names are used (e.g. "reviews"
|
||||
instead of "reviews.default.svc.cluster.local"), Istio
|
||||
will interpret the short name based on the namespace
|
||||
of the rule, not the service. A rule in the "default"
|
||||
namespace containing a host "reviews will be interpreted
|
||||
as "reviews.default.svc.cluster.local", irrespective
|
||||
of the actual namespace associated with the reviews
|
||||
service. _To avoid potential misconfigurations, it
|
||||
is recommended to always use fully qualified domain
|
||||
names over short names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host that is
|
||||
being addressed. If a service exposes only a single
|
||||
port it is not required to explicitly select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the service.
|
||||
Applicable only to services within the mesh. The subset
|
||||
must be defined in a corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
weight:
|
||||
description: REQUIRED. The proportion of traffic to be forwarded
|
||||
to the service version. (0-100). Sum of weights across
|
||||
destinations SHOULD BE == 100. If there is only destination
|
||||
in a rule, the weight value is assumed to be 100.
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- destination
|
||||
- weight
|
||||
type: object
|
||||
type: array
|
||||
timeout:
|
||||
description: Timeout for HTTP requests.
|
||||
type: string
|
||||
websocketUpgrade:
|
||||
description: Indicates that a HTTP/1.1 client connection to this
|
||||
particular route should be allowed (and expected) to upgrade
|
||||
to a WebSocket connection. The default is false. Istio's reference
|
||||
sidecar implementation (Envoy) expects the first request to
|
||||
this route to contain the WebSocket upgrade headers. Otherwise,
|
||||
the request will be rejected. Note that Websocket allows secondary
|
||||
protocol negotiation which may then be subject to further routing
|
||||
rules based on the protocol selected.
|
||||
type: boolean
|
||||
type: object
|
||||
type: array
|
||||
tcp:
|
||||
description: An ordered list of route rules for TCP traffic. The first
|
||||
rule matching an incoming request is used.
|
||||
items:
|
||||
properties:
|
||||
match:
|
||||
description: Match conditions to be satisfied for the rule to
|
||||
be activated. All conditions inside a single match block have
|
||||
AND semantics, while the list of match blocks have OR semantics.
|
||||
The rule is matched if any one of the match blocks succeed.
|
||||
items:
|
||||
properties:
|
||||
destinationSubnets:
|
||||
description: IPv4 or IPv6 ip address of destination with
|
||||
optional subnet. E.g., a.b.c.d/xx form or just a.b.c.d.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
gateways:
|
||||
description: Names of gateways where the rule should be
|
||||
applied to. Gateway names at the top of the VirtualService
|
||||
(if any) are overridden. The gateway match is independent
|
||||
of sourceLabels.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
port:
|
||||
description: Specifies the port on the host that is being
|
||||
addressed. Many services only expose a single port or
|
||||
label ports with the protocols they support, in these
|
||||
cases it is not required to explicitly select the port.
|
||||
format: int64
|
||||
type: integer
|
||||
sourceLabels:
|
||||
description: One or more labels that constrain the applicability
|
||||
of a rule to workloads with the given labels. If the VirtualService
|
||||
has a list of gateways specified at the top, it should
|
||||
include the reserved gateway `mesh` in order for this
|
||||
field to be applicable.
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
route:
|
||||
description: The destinations to which the connection should be
|
||||
forwarded to. Weights must add to 100%.
|
||||
items:
|
||||
properties:
|
||||
destination:
|
||||
description: REQUIRED. Destination uniquely identifies the
|
||||
instances of a service to which the request/connection
|
||||
should be forwarded to.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service from the
|
||||
service registry. Service names are looked up from
|
||||
the platform''s service registry (e.g., Kubernetes
|
||||
services, Consul services, etc.) and from the hosts
|
||||
declared by [ServiceEntry](#ServiceEntry). Traffic
|
||||
forwarded to destinations that are not found in either
|
||||
of the two, will be dropped. *Note for Kubernetes
|
||||
users*: When short names are used (e.g. "reviews"
|
||||
instead of "reviews.default.svc.cluster.local"), Istio
|
||||
will interpret the short name based on the namespace
|
||||
of the rule, not the service. A rule in the "default"
|
||||
namespace containing a host "reviews will be interpreted
|
||||
as "reviews.default.svc.cluster.local", irrespective
|
||||
of the actual namespace associated with the reviews
|
||||
service. _To avoid potential misconfigurations, it
|
||||
is recommended to always use fully qualified domain
|
||||
names over short names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host that is
|
||||
being addressed. If a service exposes only a single
|
||||
port it is not required to explicitly select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the service.
|
||||
Applicable only to services within the mesh. The subset
|
||||
must be defined in a corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
weight:
|
||||
description: REQUIRED. The proportion of traffic to be forwarded
|
||||
to the service version. (0-100). Sum of weights across
|
||||
destinations SHOULD BE == 100. If there is only destination
|
||||
in a rule, the weight value is assumed to be 100.
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- destination
|
||||
- weight
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- match
|
||||
- route
|
||||
type: object
|
||||
type: array
|
||||
tls:
|
||||
items:
|
||||
properties:
|
||||
match:
|
||||
description: REQUIRED. Match conditions to be satisfied for the
|
||||
rule to be activated. All conditions inside a single match block
|
||||
have AND semantics, while the list of match blocks have OR semantics.
|
||||
The rule is matched if any one of the match blocks succeed.
|
||||
items:
|
||||
properties:
|
||||
destinationSubnets:
|
||||
description: IPv4 or IPv6 ip addresses of destination with
|
||||
optional subnet. E.g., a.b.c.d/xx form or just a.b.c.d.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
gateways:
|
||||
description: Names of gateways where the rule should be
|
||||
applied to. Gateway names at the top of the VirtualService
|
||||
(if any) are overridden. The gateway match is independent
|
||||
of sourceLabels.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
port:
|
||||
description: Specifies the port on the host that is being
|
||||
addressed. Many services only expose a single port or
|
||||
label ports with the protocols they support, in these
|
||||
cases it is not required to explicitly select the port.
|
||||
format: int64
|
||||
type: integer
|
||||
sniHosts:
|
||||
description: REQUIRED. SNI (server name indicator) to match
|
||||
on. Wildcard prefixes can be used in the SNI value, e.g.,
|
||||
*.com will match foo.example.com as well as example.com.
|
||||
An SNI value must be a subset (i.e., fall within the domain)
|
||||
of the corresponding virtual service's hosts
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
sourceLabels:
|
||||
description: One or more labels that constrain the applicability
|
||||
of a rule to workloads with the given labels. If the VirtualService
|
||||
has a list of gateways specified at the top, it should
|
||||
include the reserved gateway `mesh` in order for this
|
||||
field to be applicable.
|
||||
type: object
|
||||
required:
|
||||
- sniHosts
|
||||
type: object
|
||||
type: array
|
||||
route:
|
||||
description: The destination to which the connection should be
|
||||
forwarded to.
|
||||
items:
|
||||
properties:
|
||||
destination:
|
||||
description: REQUIRED. Destination uniquely identifies the
|
||||
instances of a service to which the request/connection
|
||||
should be forwarded to.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service from the
|
||||
service registry. Service names are looked up from
|
||||
the platform''s service registry (e.g., Kubernetes
|
||||
services, Consul services, etc.) and from the hosts
|
||||
declared by [ServiceEntry](#ServiceEntry). Traffic
|
||||
forwarded to destinations that are not found in either
|
||||
of the two, will be dropped. *Note for Kubernetes
|
||||
users*: When short names are used (e.g. "reviews"
|
||||
instead of "reviews.default.svc.cluster.local"), Istio
|
||||
will interpret the short name based on the namespace
|
||||
of the rule, not the service. A rule in the "default"
|
||||
namespace containing a host "reviews will be interpreted
|
||||
as "reviews.default.svc.cluster.local", irrespective
|
||||
of the actual namespace associated with the reviews
|
||||
service. _To avoid potential misconfigurations, it
|
||||
is recommended to always use fully qualified domain
|
||||
names over short names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host that is
|
||||
being addressed. If a service exposes only a single
|
||||
port it is not required to explicitly select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the service.
|
||||
Applicable only to services within the mesh. The subset
|
||||
must be defined in a corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
weight:
|
||||
description: REQUIRED. The proportion of traffic to be forwarded
|
||||
to the service version. (0-100). Sum of weights across
|
||||
destinations SHOULD BE == 100. If there is only destination
|
||||
in a rule, the weight value is assumed to be 100.
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- destination
|
||||
- weight
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- match
|
||||
- route
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- hosts
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
version: v1alpha3
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
762
config/crds/servicemesh_v1alpha2_strategy.yaml
Normal file
762
config/crds/servicemesh_v1alpha2_strategy.yaml
Normal file
@@ -0,0 +1,762 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: strategies.servicemesh.kubesphere.io
|
||||
spec:
|
||||
group: servicemesh.kubesphere.io
|
||||
names:
|
||||
kind: Strategy
|
||||
plural: strategies
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
properties:
|
||||
paused:
|
||||
description: Indicates that the strategy is paused and will not be processed
|
||||
by the strategy controller
|
||||
type: boolean
|
||||
selector:
|
||||
description: Label selector for virtual services.
|
||||
type: object
|
||||
template:
|
||||
description: Template describes the virtual service that will be created.
|
||||
properties:
|
||||
metadata:
|
||||
description: Metadata of the virtual services created from this
|
||||
template
|
||||
type: object
|
||||
spec:
|
||||
description: Spec indicates the behavior of a virtual service.
|
||||
properties:
|
||||
gateways:
|
||||
description: The names of gateways and sidecars that should
|
||||
apply these routes. A single VirtualService is used for sidecars
|
||||
inside the mesh as well as for one or more gateways. The selection
|
||||
condition imposed by this field can be overridden using the
|
||||
source field in the match conditions of HTTP/TCP routes. The
|
||||
reserved word "mesh" is used to imply all the sidecars in
|
||||
the mesh. When this field is omitted, the default gateway
|
||||
("mesh") will be used, which would apply the rule to all sidecars
|
||||
in the mesh. If a list of gateway names is provided, the rules
|
||||
will apply only to the gateways. To apply the rules to both
|
||||
gateways and sidecars, specify "mesh" as one of the gateway
|
||||
names.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
hosts:
|
||||
description: REQUIRED. The destination address for traffic captured
|
||||
by this virtual service. Could be a DNS name with wildcard
|
||||
prefix or a CIDR prefix. Depending on the platform, short-names
|
||||
can also be used instead of a FQDN (i.e. has no dots in the
|
||||
name). In such a scenario, the FQDN of the host would be derived
|
||||
based on the underlying platform. For example on Kubernetes,
|
||||
when hosts contains a short name, Istio will interpret the
|
||||
short name based on the namespace of the rule. Thus, when
|
||||
a client namespace applies a rule in the "default" namespace
|
||||
containing a name "reviews, Istio will setup routes to the
|
||||
"reviews.default.svc.cluster.local" service. However, if a
|
||||
different name such as "reviews.sales.svc.cluster.local" is
|
||||
used, it would be treated as a FQDN during virtual host matching.
|
||||
In Consul, a plain service name would be resolved to the FQDN
|
||||
"reviews.service.consul". Note that the hosts field applies
|
||||
to both HTTP and TCP services. Service inside the mesh, i.e.,
|
||||
those found in the service registry, must always be referred
|
||||
to using their alphanumeric names. IP addresses or CIDR prefixes
|
||||
are allowed only for services defined via the Gateway.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
http:
|
||||
description: An ordered list of route rules for HTTP traffic.
|
||||
The first rule matching an incoming request is used.
|
||||
items:
|
||||
properties:
|
||||
appendHeaders:
|
||||
description: Additional HTTP headers to add before forwarding
|
||||
a request to the destination service.
|
||||
type: object
|
||||
corsPolicy:
|
||||
description: Cross-Origin Resource Sharing policy
|
||||
properties:
|
||||
allowCredentials:
|
||||
description: Indicates whether the caller is allowed
|
||||
to send the actual request (not the preflight) using
|
||||
credentials. Translates to Access-Control-Allow-Credentials
|
||||
header.
|
||||
type: boolean
|
||||
allowHeaders:
|
||||
description: List of HTTP headers that can be used
|
||||
when requesting the resource. Serialized to Access-Control-Allow-Methods
|
||||
header.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowMethods:
|
||||
description: List of HTTP methods allowed to access
|
||||
the resource. The content will be serialized into
|
||||
the Access-Control-Allow-Methods header.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowOrigin:
|
||||
description: The list of origins that are allowed
|
||||
to perform CORS requests. The content will be serialized
|
||||
into the Access-Control-Allow-Origin header. Wildcard
|
||||
* will allow all origins.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
exposeHeaders:
|
||||
description: A white list of HTTP headers that the
|
||||
browsers are allowed to access. Serialized into
|
||||
Access-Control-Expose-Headers header.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
maxAge:
|
||||
description: Specifies how long the the results of
|
||||
a preflight request can be cached. Translates to
|
||||
the Access-Control-Max-Age header.
|
||||
type: string
|
||||
type: object
|
||||
fault:
|
||||
description: Fault injection policy to apply on HTTP traffic.
|
||||
properties:
|
||||
abort:
|
||||
description: Abort Http request attempts and return
|
||||
error codes back to downstream service, giving the
|
||||
impression that the upstream service is faulty.
|
||||
properties:
|
||||
httpStatus:
|
||||
description: REQUIRED. HTTP status code to use
|
||||
to abort the Http request.
|
||||
format: int64
|
||||
type: integer
|
||||
percent:
|
||||
description: Percentage of requests to be aborted
|
||||
with the error code provided (0-100).
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- httpStatus
|
||||
type: object
|
||||
delay:
|
||||
description: Delay requests before forwarding, emulating
|
||||
various failures such as network issues, overloaded
|
||||
upstream service, etc.
|
||||
properties:
|
||||
exponentialDelay:
|
||||
description: (-- Add a delay (based on an exponential
|
||||
function) before forwarding the request. mean
|
||||
delay needed to derive the exponential delay
|
||||
values --)
|
||||
type: string
|
||||
fixedDelay:
|
||||
description: 'REQUIRED. Add a fixed delay before
|
||||
forwarding the request. Format: 1h/1m/1s/1ms.
|
||||
MUST be >=1ms.'
|
||||
type: string
|
||||
percent:
|
||||
description: Percentage of requests on which the
|
||||
delay will be injected (0-100).
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- fixedDelay
|
||||
type: object
|
||||
type: object
|
||||
match:
|
||||
description: Match conditions to be satisfied for the
|
||||
rule to be activated. All conditions inside a single
|
||||
match block have AND semantics, while the list of match
|
||||
blocks have OR semantics. The rule is matched if any
|
||||
one of the match blocks succeed.
|
||||
items:
|
||||
properties:
|
||||
authority:
|
||||
description: 'HTTP Authority values are case-sensitive
|
||||
and formatted as follows: - `exact: "value"`
|
||||
for exact string match - `prefix: "value"` for
|
||||
prefix-based match - `regex: "value"` for ECMAscript
|
||||
style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
gateways:
|
||||
description: Names of gateways where the rule should
|
||||
be applied to. Gateway names at the top of the
|
||||
VirtualService (if any) are overridden. The gateway
|
||||
match is independent of sourceLabels.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
headers:
|
||||
description: 'The header keys must be lowercase
|
||||
and use hyphen as the separator, e.g. _x-request-id_. Header
|
||||
values are case-sensitive and formatted as follows: -
|
||||
`exact: "value"` for exact string match - `prefix:
|
||||
"value"` for prefix-based match - `regex: "value"`
|
||||
for ECMAscript style regex-based match **Note:**
|
||||
The keys `uri`, `scheme`, `method`, and `authority`
|
||||
will be ignored.'
|
||||
type: object
|
||||
method:
|
||||
description: 'HTTP Method values are case-sensitive
|
||||
and formatted as follows: - `exact: "value"`
|
||||
for exact string match - `prefix: "value"` for
|
||||
prefix-based match - `regex: "value"` for ECMAscript
|
||||
style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
port:
|
||||
description: Specifies the ports on the host that
|
||||
is being addressed. Many services only expose
|
||||
a single port or label ports with the protocols
|
||||
they support, in these cases it is not required
|
||||
to explicitly select the port.
|
||||
format: int32
|
||||
type: integer
|
||||
scheme:
|
||||
description: 'URI Scheme values are case-sensitive
|
||||
and formatted as follows: - `exact: "value"`
|
||||
for exact string match - `prefix: "value"` for
|
||||
prefix-based match - `regex: "value"` for ECMAscript
|
||||
style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
sourceLabels:
|
||||
description: One or more labels that constrain the
|
||||
applicability of a rule to workloads with the
|
||||
given labels. If the VirtualService has a list
|
||||
of gateways specified at the top, it should include
|
||||
the reserved gateway `mesh` in order for this
|
||||
field to be applicable.
|
||||
type: object
|
||||
uri:
|
||||
description: 'URI to match values are case-sensitive
|
||||
and formatted as follows: - `exact: "value"`
|
||||
for exact string match - `prefix: "value"` for
|
||||
prefix-based match - `regex: "value"` for ECMAscript
|
||||
style regex-based match'
|
||||
properties:
|
||||
exact:
|
||||
description: exact string match
|
||||
type: string
|
||||
prefix:
|
||||
description: prefix-based match
|
||||
type: string
|
||||
regex:
|
||||
description: ECMAscript style regex-based match
|
||||
type: string
|
||||
suffix:
|
||||
description: suffix-based match.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
mirror:
|
||||
description: Mirror HTTP traffic to a another destination
|
||||
in addition to forwarding the requests to the intended
|
||||
destination. Mirrored traffic is on a best effort basis
|
||||
where the sidecar/gateway will not wait for the mirrored
|
||||
cluster to respond before returning the response from
|
||||
the original destination. Statistics will be generated
|
||||
for the mirrored destination.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service from
|
||||
the service registry. Service names are looked up
|
||||
from the platform''s service registry (e.g., Kubernetes
|
||||
services, Consul services, etc.) and from the hosts
|
||||
declared by [ServiceEntry](#ServiceEntry). Traffic
|
||||
forwarded to destinations that are not found in
|
||||
either of the two, will be dropped. *Note for Kubernetes
|
||||
users*: When short names are used (e.g. "reviews"
|
||||
instead of "reviews.default.svc.cluster.local"),
|
||||
Istio will interpret the short name based on the
|
||||
namespace of the rule, not the service. A rule in
|
||||
the "default" namespace containing a host "reviews
|
||||
will be interpreted as "reviews.default.svc.cluster.local",
|
||||
irrespective of the actual namespace associated
|
||||
with the reviews service. _To avoid potential misconfigurations,
|
||||
it is recommended to always use fully qualified
|
||||
domain names over short names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host that is
|
||||
being addressed. If a service exposes only a single
|
||||
port it is not required to explicitly select the
|
||||
port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the service.
|
||||
Applicable only to services within the mesh. The
|
||||
subset must be defined in a corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
redirect:
|
||||
description: A http rule can either redirect or forward
|
||||
(default) traffic. If traffic passthrough option is
|
||||
specified in the rule, route/redirect will be ignored.
|
||||
The redirect primitive can be used to send a HTTP 302
|
||||
redirect to a different URI or Authority.
|
||||
properties:
|
||||
authority:
|
||||
description: On a redirect, overwrite the Authority/Host
|
||||
portion of the URL with this value.
|
||||
type: string
|
||||
uri:
|
||||
description: On a redirect, overwrite the Path portion
|
||||
of the URL with this value. Note that the entire
|
||||
path will be replaced, irrespective of the request
|
||||
URI being matched as an exact path or prefix.
|
||||
type: string
|
||||
type: object
|
||||
removeResponseHeaders:
|
||||
description: Http headers to remove before returning the
|
||||
response to the caller
|
||||
type: object
|
||||
retries:
|
||||
description: Retry policy for HTTP requests.
|
||||
properties:
|
||||
attempts:
|
||||
description: REQUIRED. Number of retries for a given
|
||||
request. The interval between retries will be determined
|
||||
automatically (25ms+). Actual number of retries
|
||||
attempted depends on the httpReqTimeout.
|
||||
format: int64
|
||||
type: integer
|
||||
perTryTimeout:
|
||||
description: 'Timeout per retry attempt for a given
|
||||
request. format: 1h/1m/1s/1ms. MUST BE >=1ms.'
|
||||
type: string
|
||||
required:
|
||||
- attempts
|
||||
- perTryTimeout
|
||||
type: object
|
||||
rewrite:
|
||||
description: Rewrite HTTP URIs and Authority headers.
|
||||
Rewrite cannot be used with Redirect primitive. Rewrite
|
||||
will be performed before forwarding.
|
||||
properties:
|
||||
authority:
|
||||
description: rewrite the Authority/Host header with
|
||||
this value.
|
||||
type: string
|
||||
uri:
|
||||
description: rewrite the path (or the prefix) portion
|
||||
of the URI with this value. If the original URI
|
||||
was matched based on prefix, the value provided
|
||||
in this field will replace the corresponding matched
|
||||
prefix.
|
||||
type: string
|
||||
type: object
|
||||
route:
|
||||
description: A http rule can either redirect or forward
|
||||
(default) traffic. The forwarding target can be one
|
||||
of several versions of a service (see glossary in beginning
|
||||
of document). Weights associated with the service version
|
||||
determine the proportion of traffic it receives.
|
||||
items:
|
||||
properties:
|
||||
destination:
|
||||
description: REQUIRED. Destination uniquely identifies
|
||||
the instances of a service to which the request/connection
|
||||
should be forwarded to.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service
|
||||
from the service registry. Service names are
|
||||
looked up from the platform''s service registry
|
||||
(e.g., Kubernetes services, Consul services,
|
||||
etc.) and from the hosts declared by [ServiceEntry](#ServiceEntry).
|
||||
Traffic forwarded to destinations that are
|
||||
not found in either of the two, will be dropped. *Note
|
||||
for Kubernetes users*: When short names are
|
||||
used (e.g. "reviews" instead of "reviews.default.svc.cluster.local"),
|
||||
Istio will interpret the short name based
|
||||
on the namespace of the rule, not the service.
|
||||
A rule in the "default" namespace containing
|
||||
a host "reviews will be interpreted as "reviews.default.svc.cluster.local",
|
||||
irrespective of the actual namespace associated
|
||||
with the reviews service. _To avoid potential
|
||||
misconfigurations, it is recommended to always
|
||||
use fully qualified domain names over short
|
||||
names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host
|
||||
that is being addressed. If a service exposes
|
||||
only a single port it is not required to explicitly
|
||||
select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the
|
||||
service. Applicable only to services within
|
||||
the mesh. The subset must be defined in a
|
||||
corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
weight:
|
||||
description: REQUIRED. The proportion of traffic
|
||||
to be forwarded to the service version. (0-100).
|
||||
Sum of weights across destinations SHOULD BE ==
|
||||
100. If there is only destination in a rule, the
|
||||
weight value is assumed to be 100.
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- destination
|
||||
- weight
|
||||
type: object
|
||||
type: array
|
||||
timeout:
|
||||
description: Timeout for HTTP requests.
|
||||
type: string
|
||||
websocketUpgrade:
|
||||
description: Indicates that a HTTP/1.1 client connection
|
||||
to this particular route should be allowed (and expected)
|
||||
to upgrade to a WebSocket connection. The default is
|
||||
false. Istio's reference sidecar implementation (Envoy)
|
||||
expects the first request to this route to contain the
|
||||
WebSocket upgrade headers. Otherwise, the request will
|
||||
be rejected. Note that Websocket allows secondary protocol
|
||||
negotiation which may then be subject to further routing
|
||||
rules based on the protocol selected.
|
||||
type: boolean
|
||||
type: object
|
||||
type: array
|
||||
tcp:
|
||||
description: An ordered list of route rules for TCP traffic.
|
||||
The first rule matching an incoming request is used.
|
||||
items:
|
||||
properties:
|
||||
match:
|
||||
description: Match conditions to be satisfied for the
|
||||
rule to be activated. All conditions inside a single
|
||||
match block have AND semantics, while the list of match
|
||||
blocks have OR semantics. The rule is matched if any
|
||||
one of the match blocks succeed.
|
||||
items:
|
||||
properties:
|
||||
destinationSubnets:
|
||||
description: IPv4 or IPv6 ip address of destination
|
||||
with optional subnet. E.g., a.b.c.d/xx form or
|
||||
just a.b.c.d.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
gateways:
|
||||
description: Names of gateways where the rule should
|
||||
be applied to. Gateway names at the top of the
|
||||
VirtualService (if any) are overridden. The gateway
|
||||
match is independent of sourceLabels.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
port:
|
||||
description: Specifies the port on the host that
|
||||
is being addressed. Many services only expose
|
||||
a single port or label ports with the protocols
|
||||
they support, in these cases it is not required
|
||||
to explicitly select the port.
|
||||
format: int64
|
||||
type: integer
|
||||
sourceLabels:
|
||||
description: One or more labels that constrain the
|
||||
applicability of a rule to workloads with the
|
||||
given labels. If the VirtualService has a list
|
||||
of gateways specified at the top, it should include
|
||||
the reserved gateway `mesh` in order for this
|
||||
field to be applicable.
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
route:
|
||||
description: The destinations to which the connection
|
||||
should be forwarded to. Weights must add to 100%.
|
||||
items:
|
||||
properties:
|
||||
destination:
|
||||
description: REQUIRED. Destination uniquely identifies
|
||||
the instances of a service to which the request/connection
|
||||
should be forwarded to.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service
|
||||
from the service registry. Service names are
|
||||
looked up from the platform''s service registry
|
||||
(e.g., Kubernetes services, Consul services,
|
||||
etc.) and from the hosts declared by [ServiceEntry](#ServiceEntry).
|
||||
Traffic forwarded to destinations that are
|
||||
not found in either of the two, will be dropped. *Note
|
||||
for Kubernetes users*: When short names are
|
||||
used (e.g. "reviews" instead of "reviews.default.svc.cluster.local"),
|
||||
Istio will interpret the short name based
|
||||
on the namespace of the rule, not the service.
|
||||
A rule in the "default" namespace containing
|
||||
a host "reviews will be interpreted as "reviews.default.svc.cluster.local",
|
||||
irrespective of the actual namespace associated
|
||||
with the reviews service. _To avoid potential
|
||||
misconfigurations, it is recommended to always
|
||||
use fully qualified domain names over short
|
||||
names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host
|
||||
that is being addressed. If a service exposes
|
||||
only a single port it is not required to explicitly
|
||||
select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the
|
||||
service. Applicable only to services within
|
||||
the mesh. The subset must be defined in a
|
||||
corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
weight:
|
||||
description: REQUIRED. The proportion of traffic
|
||||
to be forwarded to the service version. (0-100).
|
||||
Sum of weights across destinations SHOULD BE ==
|
||||
100. If there is only destination in a rule, the
|
||||
weight value is assumed to be 100.
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- destination
|
||||
- weight
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- match
|
||||
- route
|
||||
type: object
|
||||
type: array
|
||||
tls:
|
||||
items:
|
||||
properties:
|
||||
match:
|
||||
description: REQUIRED. Match conditions to be satisfied
|
||||
for the rule to be activated. All conditions inside
|
||||
a single match block have AND semantics, while the list
|
||||
of match blocks have OR semantics. The rule is matched
|
||||
if any one of the match blocks succeed.
|
||||
items:
|
||||
properties:
|
||||
destinationSubnets:
|
||||
description: IPv4 or IPv6 ip addresses of destination
|
||||
with optional subnet. E.g., a.b.c.d/xx form or
|
||||
just a.b.c.d.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
gateways:
|
||||
description: Names of gateways where the rule should
|
||||
be applied to. Gateway names at the top of the
|
||||
VirtualService (if any) are overridden. The gateway
|
||||
match is independent of sourceLabels.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
port:
|
||||
description: Specifies the port on the host that
|
||||
is being addressed. Many services only expose
|
||||
a single port or label ports with the protocols
|
||||
they support, in these cases it is not required
|
||||
to explicitly select the port.
|
||||
format: int64
|
||||
type: integer
|
||||
sniHosts:
|
||||
description: REQUIRED. SNI (server name indicator)
|
||||
to match on. Wildcard prefixes can be used in
|
||||
the SNI value, e.g., *.com will match foo.example.com
|
||||
as well as example.com. An SNI value must be a
|
||||
subset (i.e., fall within the domain) of the corresponding
|
||||
virtual service's hosts
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
sourceLabels:
|
||||
description: One or more labels that constrain the
|
||||
applicability of a rule to workloads with the
|
||||
given labels. If the VirtualService has a list
|
||||
of gateways specified at the top, it should include
|
||||
the reserved gateway `mesh` in order for this
|
||||
field to be applicable.
|
||||
type: object
|
||||
required:
|
||||
- sniHosts
|
||||
type: object
|
||||
type: array
|
||||
route:
|
||||
description: The destination to which the connection should
|
||||
be forwarded to.
|
||||
items:
|
||||
properties:
|
||||
destination:
|
||||
description: REQUIRED. Destination uniquely identifies
|
||||
the instances of a service to which the request/connection
|
||||
should be forwarded to.
|
||||
properties:
|
||||
host:
|
||||
description: 'REQUIRED. The name of a service
|
||||
from the service registry. Service names are
|
||||
looked up from the platform''s service registry
|
||||
(e.g., Kubernetes services, Consul services,
|
||||
etc.) and from the hosts declared by [ServiceEntry](#ServiceEntry).
|
||||
Traffic forwarded to destinations that are
|
||||
not found in either of the two, will be dropped. *Note
|
||||
for Kubernetes users*: When short names are
|
||||
used (e.g. "reviews" instead of "reviews.default.svc.cluster.local"),
|
||||
Istio will interpret the short name based
|
||||
on the namespace of the rule, not the service.
|
||||
A rule in the "default" namespace containing
|
||||
a host "reviews will be interpreted as "reviews.default.svc.cluster.local",
|
||||
irrespective of the actual namespace associated
|
||||
with the reviews service. _To avoid potential
|
||||
misconfigurations, it is recommended to always
|
||||
use fully qualified domain names over short
|
||||
names._'
|
||||
type: string
|
||||
port:
|
||||
description: Specifies the port on the host
|
||||
that is being addressed. If a service exposes
|
||||
only a single port it is not required to explicitly
|
||||
select the port.
|
||||
properties:
|
||||
name:
|
||||
description: Valid port name
|
||||
type: string
|
||||
number:
|
||||
description: Valid port number
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
subset:
|
||||
description: The name of a subset within the
|
||||
service. Applicable only to services within
|
||||
the mesh. The subset must be defined in a
|
||||
corresponding DestinationRule.
|
||||
type: string
|
||||
required:
|
||||
- host
|
||||
type: object
|
||||
weight:
|
||||
description: REQUIRED. The proportion of traffic
|
||||
to be forwarded to the service version. (0-100).
|
||||
Sum of weights across destinations SHOULD BE ==
|
||||
100. If there is only destination in a rule, the
|
||||
weight value is assumed to be 100.
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- destination
|
||||
- weight
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- match
|
||||
- route
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- hosts
|
||||
type: object
|
||||
type: object
|
||||
type:
|
||||
description: Strategy type
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
type: object
|
||||
version: v1alpha2
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -24,9 +24,9 @@ resources:
|
||||
# Comment the following 3 lines if you want to disable
|
||||
# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
|
||||
# which protects your /metrics endpoint.
|
||||
- ../rbac/auth_proxy_service.yaml
|
||||
- ../rbac/auth_proxy_role.yaml
|
||||
- ../rbac/auth_proxy_role_binding.yaml
|
||||
#- ../rbac/auth_proxy_service.yaml
|
||||
#- ../rbac/auth_proxy_role.yaml
|
||||
#- ../rbac/auth_proxy_role_binding.yaml
|
||||
|
||||
patches:
|
||||
- manager_image_patch.yaml
|
||||
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kube-rbac-proxy
|
||||
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0
|
||||
image: quay.io/coreos/kube-rbac-proxy:v0.4.0
|
||||
args:
|
||||
- "--secure-listen-address=0.0.0.0:8443"
|
||||
- "--upstream=http://127.0.0.1:8080/"
|
||||
|
||||
@@ -8,5 +8,5 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
# Change the value of image field below to your controller image URL
|
||||
- image: IMAGE_URL
|
||||
- image: kubespheredev/controller-manager:latest
|
||||
name: manager
|
||||
|
||||
@@ -43,8 +43,8 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- /manager
|
||||
image: controller:latest
|
||||
- ./controller-manager
|
||||
image: kubespheredev/controller-manager:latest
|
||||
imagePullPolicy: Always
|
||||
name: manager
|
||||
env:
|
||||
|
||||
87
config/rbac/rbac_role.yaml
Normal file
87
config/rbac/rbac_role.yaml
Normal file
@@ -0,0 +1,87 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.istio.io
|
||||
resources:
|
||||
- virtualservices
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- networking.istio.io
|
||||
resources:
|
||||
- virtualservices/status
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- servicemesh.kubesphere.io
|
||||
resources:
|
||||
- strategies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- servicemesh.kubesphere.io
|
||||
resources:
|
||||
- strategies/status
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- admissionregistration.k8s.io
|
||||
resources:
|
||||
- mutatingwebhookconfigurations
|
||||
- validatingwebhookconfigurations
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
13
config/rbac/rbac_role_binding.yaml
Normal file
13
config/rbac/rbac_role_binding.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: manager-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: manager-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: system
|
||||
26
config/samples/servicemesh_v1alpha2_strategy.yaml
Normal file
26
config/samples/servicemesh_v1alpha2_strategy.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
apiVersion: servicemesh.kubesphere.io/v1alpha2
|
||||
kind: Strategy
|
||||
metadata:
|
||||
labels:
|
||||
controller-tools.k8s.io: "1.0"
|
||||
name: strategy-sample
|
||||
spec:
|
||||
# Add fields here
|
||||
type: Canary
|
||||
selector:
|
||||
matchLabels:
|
||||
"servicemesh.kubesphere.io/type": "canary"
|
||||
template:
|
||||
spec:
|
||||
hosts:
|
||||
- details
|
||||
http:
|
||||
- route:
|
||||
- destination:
|
||||
host: "details"
|
||||
subset: v1
|
||||
weight: 60
|
||||
- destination:
|
||||
host: "details"
|
||||
subset: v2
|
||||
weight: 40
|
||||
16
hack/custom-boilerplate.go.txt
Normal file
16
hack/custom-boilerplate.go.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
@@ -29,8 +29,8 @@ fi
|
||||
|
||||
ROOTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
OUTPUT_DIR=${ROOTDIR}/bin
|
||||
BUILDPATH=${ROOTDIR}/${1:?"path to build"}
|
||||
OUTPUT_DIR=bin
|
||||
BUILDPATH=./${1:?"path to build"}
|
||||
OUT=${OUTPUT_DIR}/${1:?"output path"}
|
||||
|
||||
set -e
|
||||
@@ -42,4 +42,4 @@ GOBINARY=${GOBINARY:-go}
|
||||
# forgoing -i (incremental build) because it will be deprecated by tool chain.
|
||||
time GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} ${GOBINARY} build \
|
||||
-o ${OUT} \
|
||||
${BUILDPATH}
|
||||
${BUILDPATH}
|
||||
|
||||
88
hack/install_kubebuilder.sh
Executable file
88
hack/install_kubebuilder.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# This file will be fetched as: curl -L https://git.io/getLatestKubebuilder | sh -
|
||||
# so it should be pure bourne shell, not bash (and not reference other scripts)
|
||||
#
|
||||
# The script fetches the latest kubebuilder release candidate and untars it.
|
||||
# It lets users to do curl -L https://git.io//getLatestKubebuilder | KUBEBUILDER_VERSION=1.0.5 sh -
|
||||
# for instance to change the version fetched.
|
||||
|
||||
# Check if the program is installed, otherwise exit
|
||||
function command_exists () {
|
||||
if ! [ -x "$(command -v $1)" ]; then
|
||||
echo "Error: $1 program is not installed." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Determine OS
|
||||
OS="$(uname)"
|
||||
case $OS in
|
||||
Darwin)
|
||||
OSEXT="darwin"
|
||||
;;
|
||||
Linux)
|
||||
OSEXT="linux"
|
||||
;;
|
||||
*)
|
||||
echo "Only OSX and Linux OS are supported !"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
HW=$(uname -m)
|
||||
case $HW in
|
||||
x86_64)
|
||||
ARCH=amd64 ;;
|
||||
*)
|
||||
echo "Only x86_64 machines are supported !"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if curl, tar commands/programs exist
|
||||
command_exists curl
|
||||
command_exists tar
|
||||
|
||||
if [ "x${KUBEBUILDER_VERSION}" = "x" ] ; then
|
||||
KUBEBUILDER_VERSION=$(curl -L -s https://api.github.com/repos/kubernetes-sigs/kubebuilder/releases/latest | \
|
||||
grep tag_name | sed "s/ *\"tag_name\": *\"\\(.*\\)\",*/\\1/")
|
||||
if [ -z "$KUBEBUILDER_VERSION" ]; then
|
||||
echo "\nUnable to fetch the latest version tag. This may be due to network access problem"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
KUBEBUILDER_VERSION=${KUBEBUILDER_VERSION#"v"}
|
||||
KUBEBUILDER_VERSION_NAME="kubebuilder_${KUBEBUILDER_VERSION}"
|
||||
KUBEBUILDER_DIR=/usr/local/kubebuilder
|
||||
|
||||
# Check if folder containing kubebuilder executable exists and is not empty
|
||||
if [ -d "$KUBEBUILDER_DIR" ]; then
|
||||
if [ "$(ls -A $KUBEBUILDER_DIR)" ]; then
|
||||
echo "\n/usr/local/kubebuilder folder is not empty. Please delete or backup it before to install ${KUBEBUILDER_VERSION_NAME}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
TMP_DIR=$(mktemp -d)
|
||||
pushd $TMP_DIR
|
||||
|
||||
# Downloading Kubebuilder compressed file using curl program
|
||||
URL="https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${KUBEBUILDER_VERSION}/${KUBEBUILDER_VERSION_NAME}_${OSEXT}_${ARCH}.tar.gz"
|
||||
echo "Downloading ${KUBEBUILDER_VERSION_NAME}\nfrom $URL\n"
|
||||
curl -L "$URL"| tar xz -C $TMP_DIR
|
||||
|
||||
echo "Downloaded executable files"
|
||||
ls "${KUBEBUILDER_VERSION_NAME}_${OSEXT}_${ARCH}/bin"
|
||||
|
||||
echo "Moving files to $KUBEBUILDER_DIR folder\n"
|
||||
mv ${KUBEBUILDER_VERSION_NAME}_${OSEXT}_${ARCH} kubebuilder && sudo mv -f kubebuilder /usr/local/
|
||||
|
||||
echo "Add kubebuilder to your path; e.g copy paste in your shell and/or edit your ~/.profile file"
|
||||
echo "export PATH=\$PATH:/usr/local/kubebuilder/bin"
|
||||
popd
|
||||
rm -rf $TMP_DIR
|
||||
|
||||
export PATH=$PATH:/usr/local/kubebuilder/bin
|
||||
@@ -20,6 +20,9 @@ package authenticate
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -46,6 +49,10 @@ type User struct {
|
||||
Extra *map[string]interface{} `json:"extra,omitempty"`
|
||||
}
|
||||
|
||||
var requestInfoFactory = request.RequestInfoFactory{
|
||||
APIPrefixes: sets.NewString("api", "apis"),
|
||||
GrouplessAPIPrefixes: sets.NewString("api")}
|
||||
|
||||
func (h Auth) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error) {
|
||||
for _, path := range h.Rule.ExceptedPath {
|
||||
if httpserver.Path(req.URL.Path).Matches(path) {
|
||||
@@ -91,10 +98,13 @@ func (h Auth) InjectContext(req *http.Request, token *jwt.Token) (*http.Request,
|
||||
}
|
||||
}
|
||||
|
||||
usr := &user.DefaultInfo{}
|
||||
|
||||
username, ok := payLoad["username"].(string)
|
||||
|
||||
if ok && username != "" {
|
||||
req.Header.Set("X-Token-Username", username)
|
||||
usr.Name = username
|
||||
}
|
||||
|
||||
uid := payLoad["uid"]
|
||||
@@ -103,19 +113,38 @@ func (h Auth) InjectContext(req *http.Request, token *jwt.Token) (*http.Request,
|
||||
switch uid.(type) {
|
||||
case int:
|
||||
req.Header.Set("X-Token-UID", strconv.Itoa(uid.(int)))
|
||||
usr.UID = strconv.Itoa(uid.(int))
|
||||
break
|
||||
case string:
|
||||
req.Header.Set("X-Token-UID", uid.(string))
|
||||
usr.UID = uid.(string)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
groups, ok := payLoad["groups"].([]string)
|
||||
|
||||
if ok && len(groups) > 0 {
|
||||
req.Header.Set("X-Token-Groups", strings.Join(groups, ","))
|
||||
usr.Groups = groups
|
||||
}
|
||||
|
||||
// hard code, support jenkins auth plugin
|
||||
if httpserver.Path(req.URL.Path).Matches("/apis/jenkins.kubesphere.io") || httpserver.Path(req.URL.Path).Matches("job") {
|
||||
req.SetBasicAuth(username, token.Raw)
|
||||
}
|
||||
|
||||
context := request.WithUser(req.Context(), usr)
|
||||
|
||||
requestInfo, err := requestInfoFactory.NewRequestInfo(req)
|
||||
|
||||
if err == nil {
|
||||
context = request.WithRequestInfo(context, requestInfo)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(context)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,9 @@ func (c Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request) (int,
|
||||
|
||||
attrs, err := getAuthorizerAttributes(r.Context())
|
||||
|
||||
// without authenticate, no requestInfo found in the context
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
return c.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
permitted, err := permissionValidate(attrs)
|
||||
|
||||
35
pkg/apis/addtoscheme_servicemesh_v1alpha2.go
Normal file
35
pkg/apis/addtoscheme_servicemesh_v1alpha2.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apis
|
||||
|
||||
import (
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
|
||||
"sigs.k8s.io/application/pkg/apis/app/v1beta1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
|
||||
AddToSchemes = append(AddToSchemes, v1alpha2.SchemeBuilder.AddToScheme)
|
||||
|
||||
// Register networking.istio.io/v1alpha3
|
||||
AddToSchemes = append(AddToSchemes, v1alpha3.SchemeBuilder.AddToScheme)
|
||||
|
||||
// Register application scheme
|
||||
AddToSchemes = append(AddToSchemes, v1beta1.SchemeBuilder.AddToScheme)
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package v1alpha2
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/iam"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
@@ -209,6 +210,37 @@ func addWebService(c *restful.Container) error {
|
||||
Doc("Delete user from workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
|
||||
tags = []string{"unstable"}
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
|
||||
To(iam.UserNamespaceListHandler).
|
||||
Doc("Get namespace list").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Writes(models.PageableResponse{}))
|
||||
ws.Route(ws.POST("/workspaces/{name}/namespaces").
|
||||
To(iam.NamespaceCreateHandler).
|
||||
Doc("Create namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Writes(v1.Namespace{}))
|
||||
ws.Route(ws.DELETE("/workspaces/{name}/namespaces/{namespace}").To(iam.NamespaceDeleteHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
ws.Route(ws.GET("/workspaces/{name}/namespaces/{namespace}").To(iam.NamespaceCheckHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
ws.Route(ws.GET("/namespaces/{namespace}").To(iam.NamespaceCheckHandler))
|
||||
|
||||
// TODO move to /apis/resources.kubesphere.io/workspaces/{workspace}/members/{username}
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}/namespaces").To(iam.NamespacesListHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
ws.Route(ws.GET("/workspaces/{name}/members/{username}/devops").To(iam.DevOpsProjectHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
// TODO /workspaces/{name}/roles/{role}
|
||||
ws.Route(ws.GET("/workspaces/{name}/roles/{role}").To(iam.WorkspaceRoles).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
// TODO move to /apis/resources.kubesphere.io/devops
|
||||
ws.Route(ws.GET("/workspaces/{name}/devops").To(iam.DevOpsProjectHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
ws.Route(ws.POST("/workspaces/{name}/devops").To(iam.DevOpsProjectCreateHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
ws.Route(ws.DELETE("/workspaces/{name}/devops/{id}").To(iam.DevOpsProjectDeleteHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
|
||||
// TODO merge into /groups
|
||||
ws.Route(ws.GET("/groups/count").To(iam.CountHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
|
||||
ws.Route(ws.GET("/users/{name}/namespaces").To(iam.NamespacesListHandler).Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
|
||||
c.Add(ws)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,13 +83,29 @@ func addWebService(c *restful.Container) error {
|
||||
DataFormat("limit=%d,page=%d").
|
||||
DefaultValue("limit=10,page=1"))
|
||||
|
||||
tags = []string{"Applications"}
|
||||
|
||||
webservice.Route(webservice.GET("/applications").
|
||||
To(resources.ApplicationHandler).
|
||||
Writes(models.PageableResponse{}).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Cluster level resource query").
|
||||
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
|
||||
Required(false).
|
||||
DataFormat("key=value,key~value").
|
||||
DefaultValue("")).
|
||||
Param(webservice.QueryParameter("cluster_id", "cluster id")).
|
||||
Param(webservice.QueryParameter("runtime_id", "runtime id")).
|
||||
Param(webservice.QueryParameter(params.PagingParam, "page").
|
||||
Required(false).
|
||||
DataFormat("limit=%d,page=%d").
|
||||
DefaultValue("limit=10,page=1")))
|
||||
|
||||
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").
|
||||
To(resources.GetPvcListBySc).
|
||||
Doc("get user's kubectl pod").
|
||||
Param(webservice.PathParameter("username", "username")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags))
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}/pods").
|
||||
To(resources.GetPodListByPvc))
|
||||
|
||||
tags = []string{"User resources"}
|
||||
|
||||
|
||||
18
pkg/apis/servicemesh/group.go
Normal file
18
pkg/apis/servicemesh/group.go
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package servicemesh contains servicemesh API versions
|
||||
package servicemesh
|
||||
16
pkg/apis/servicemesh/metrics/install/install.go
Normal file
16
pkg/apis/servicemesh/metrics/install/install.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/apis/servicemesh/metrics/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Install(runtime.Container)
|
||||
}
|
||||
|
||||
func Install(c *restful.Container) {
|
||||
urlruntime.Must(v1alpha2.AddToContainer(c))
|
||||
}
|
||||
132
pkg/apis/servicemesh/metrics/v1alpha2/register.go
Normal file
132
pkg/apis/servicemesh/metrics/v1alpha2/register.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/servicemesh/metrics"
|
||||
"kubesphere.io/kubesphere/pkg/errors"
|
||||
)
|
||||
|
||||
const GroupName = "servicemesh.kubesphere.io"
|
||||
|
||||
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
|
||||
|
||||
var (
|
||||
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
|
||||
AddToContainer = WebServiceBuilder.AddToContainer
|
||||
)
|
||||
|
||||
func addWebService(c *restful.Container) error {
|
||||
|
||||
tags := []string{"ServiceMesh"}
|
||||
|
||||
webservice := runtime.NewWebService(GroupVersion)
|
||||
|
||||
// Get service metrics
|
||||
// GET /namespaces/{namespace}/services/{service}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/metrics").
|
||||
To(metrics.GetServiceMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get app metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace")).
|
||||
Param(webservice.PathParameter("service", "name of the service")).
|
||||
Param(webservice.QueryParameter("filters[]", "type of metrics type, e.g. request_count, request_duration, request_error_count")).
|
||||
Param(webservice.QueryParameter("queryTime", "from which UNIX time to extract metrics")).
|
||||
Param(webservice.QueryParameter("duration", "metrics duration, in seconds")).
|
||||
Param(webservice.QueryParameter("step", "metrics step")).
|
||||
Param(webservice.QueryParameter("rateInterval", "metrics rate intervals, e.g. 20s")).
|
||||
Param(webservice.QueryParameter("quantiles[]", "metrics quantiles, 0.5, 0.9, 0.99")).
|
||||
Param(webservice.QueryParameter("byLabels[]", "by which labels to group node, e.g. source_workload, destination_service_name")).
|
||||
Param(webservice.QueryParameter("requestProtocol", "request protocol, http/tcp")).
|
||||
Param(webservice.QueryParameter("reporter", "destination")).
|
||||
Writes(errors.Error{})).Produces(restful.MIME_JSON)
|
||||
|
||||
// Get app metrics
|
||||
// Get /namespaces/{namespace}/apps/{app}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/metrics").
|
||||
To(metrics.GetAppMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get app metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace")).
|
||||
Param(webservice.PathParameter("app", "name of the workload label app value")).
|
||||
Param(webservice.QueryParameter("filters[]", "type of metrics type, e.g. request_count, request_duration, request_error_count")).
|
||||
Param(webservice.QueryParameter("queryTime", "from which UNIX time to extract metrics")).
|
||||
Param(webservice.QueryParameter("duration", "metrics duration, in seconds")).
|
||||
Param(webservice.QueryParameter("step", "metrics step")).
|
||||
Param(webservice.QueryParameter("rateInterval", "metrics rate intervals, e.g. 20s")).
|
||||
Param(webservice.QueryParameter("quantiles[]", "metrics quantiles, 0.5, 0.9, 0.99")).
|
||||
Param(webservice.QueryParameter("byLabels[]", "by which labels to group node, e.g. source_workload, destination_service_name")).
|
||||
Param(webservice.QueryParameter("requestProtocol", "request protocol, http/tcp")).
|
||||
Param(webservice.QueryParameter("reporter", "destination")).
|
||||
Writes(errors.Error{})).Produces(restful.MIME_JSON)
|
||||
|
||||
// Get workload metrics
|
||||
// Get /namespaces/{namespace}/workloads/{workload}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/metrics").
|
||||
To(metrics.GetWorkloadMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get workload metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace")).
|
||||
Param(webservice.PathParameter("workload", "name of the workload")).
|
||||
Param(webservice.QueryParameter("filters[]", "type of metrics type, e.g. request_count, request_duration, request_error_count")).
|
||||
Param(webservice.QueryParameter("queryTime", "from which UNIX time to extract metrics")).
|
||||
Param(webservice.QueryParameter("duration", "metrics duration, in seconds")).
|
||||
Param(webservice.QueryParameter("step", "metrics step")).
|
||||
Param(webservice.QueryParameter("rateInterval", "metrics rate intervals, e.g. 20s")).
|
||||
Param(webservice.QueryParameter("quantiles[]", "metrics quantiles, 0.5, 0.9, 0.99")).
|
||||
Param(webservice.QueryParameter("byLabels[]", "by which labels to group node, e.g. source_workload, destination_service_name")).
|
||||
Param(webservice.QueryParameter("requestProtocol", "request protocol, http/tcp")).
|
||||
Param(webservice.QueryParameter("reporter", "destination")).
|
||||
Writes(errors.Error{})).Produces(restful.MIME_JSON)
|
||||
|
||||
// Get namespace metrics
|
||||
// Get /namespaces/{namespace}/metrics
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/metrics").
|
||||
To(metrics.GetNamespaceMetrics).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get workload metrics from a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of the namespace")).
|
||||
Param(webservice.QueryParameter("filters[]", "type of metrics type, e.g. request_count, request_duration, request_error_count")).
|
||||
Param(webservice.QueryParameter("queryTime", "from which UNIX time to extract metrics")).
|
||||
Param(webservice.QueryParameter("duration", "metrics duration, in seconds")).
|
||||
Param(webservice.QueryParameter("step", "metrics step")).
|
||||
Param(webservice.QueryParameter("rateInterval", "metrics rate intervals, e.g. 20s")).
|
||||
Param(webservice.QueryParameter("quantiles[]", "metrics quantiles, 0.5, 0.9, 0.99")).
|
||||
Param(webservice.QueryParameter("byLabels[]", "by which labels to group node, e.g. source_workload, destination_service_name")).
|
||||
Param(webservice.QueryParameter("requestProtocol", "request protocol, http/tcp")).
|
||||
Param(webservice.QueryParameter("reporter", "destination")).
|
||||
Writes(errors.Error{})).Produces(restful.MIME_JSON)
|
||||
|
||||
// Get namespace graph
|
||||
// Get /namespaces/{namespace}/graph
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/graph").
|
||||
To(metrics.GetNamespaceGraph).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get service graph for a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace")).
|
||||
Param(webservice.QueryParameter("graphType", "type of the generated service graph, eg. ")).
|
||||
Param(webservice.QueryParameter("groupBy", "group nodes by kind")).
|
||||
Param(webservice.QueryParameter("queryTime", "from which time point, default now")).
|
||||
Param(webservice.QueryParameter("injectServiceNodes", "whether to inject service ndoes")).
|
||||
Writes(errors.Error{})).Produces(restful.MIME_JSON)
|
||||
|
||||
// Get namespaces graph, for multiple namespaces
|
||||
// Get /namespaces/graph
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/graph").
|
||||
To(metrics.GetNamespacesGraph).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Doc("Get service graph for a specific namespace").
|
||||
Param(webservice.PathParameter("namespace", "name of a namespace")).
|
||||
Param(webservice.QueryParameter("graphType", "type of the generated service graph, eg. ")).
|
||||
Param(webservice.QueryParameter("groupBy", "group nodes by kind")).
|
||||
Param(webservice.QueryParameter("queryTime", "from which time point, default now")).
|
||||
Param(webservice.QueryParameter("injectServiceNodes", "whether to inject service ndoes")).
|
||||
Param(webservice.QueryParameter("namespaces", "names of namespaces")).
|
||||
Writes(errors.Error{})).Produces(restful.MIME_JSON)
|
||||
|
||||
c.Add(webservice)
|
||||
|
||||
return nil
|
||||
}
|
||||
23
pkg/apis/servicemesh/v1alpha2/doc.go
Normal file
23
pkg/apis/servicemesh/v1alpha2/doc.go
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package v1alpha2 contains API Schema definitions for the servicemesh v1alpha2 API group
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:deepcopy-gen=package,register
|
||||
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/servicemesh
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=servicemesh.kubesphere.io
|
||||
package v1alpha2
|
||||
46
pkg/apis/servicemesh/v1alpha2/register.go
Normal file
46
pkg/apis/servicemesh/v1alpha2/register.go
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// NOTE: Boilerplate only. Ignore this file.
|
||||
|
||||
// Package v1alpha2 contains API Schema definitions for the servicemesh v1alpha2 API group
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:deepcopy-gen=package,register
|
||||
// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/servicemesh
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=servicemesh.kubesphere.io
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: "servicemesh.kubesphere.io", Version: "v1alpha2"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
|
||||
|
||||
// AddToScheme is required by pkg/client/...
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// Resource is required by pkg/client/listers/...
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
151
pkg/apis/servicemesh/v1alpha2/strategy_types.go
Normal file
151
pkg/apis/servicemesh/v1alpha2/strategy_types.go
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
type StrategyType string
|
||||
|
||||
const (
|
||||
|
||||
// Canary strategy type
|
||||
CanaryType StrategyType = "Canary"
|
||||
|
||||
// BlueGreen strategy type
|
||||
BlueGreenType StrategyType = "BlueGreen"
|
||||
|
||||
// Mirror strategy type
|
||||
Mirror StrategyType = "Mirror"
|
||||
)
|
||||
|
||||
// StrategySpec defines the desired state of Strategy
|
||||
type StrategySpec struct {
|
||||
|
||||
// Strategy type
|
||||
Type StrategyType `json:"type,omitempty"`
|
||||
|
||||
// Label selector for virtual services.
|
||||
// +optional
|
||||
Selector *metav1.LabelSelector `json:"selector,omitempty"`
|
||||
|
||||
// Template describes the virtual service that will be created.
|
||||
Template VirtualServiceTemplateSpec `json:"template,omitempty"`
|
||||
|
||||
// Indicates that the strategy is paused and will not be processed
|
||||
// by the strategy controller
|
||||
Paused bool `json:"paused,omitempty"`
|
||||
}
|
||||
|
||||
// VirtualServiceTemplateSpec
|
||||
type VirtualServiceTemplateSpec struct {
|
||||
|
||||
// Metadata of the virtual services created from this template
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec indicates the behavior of a virtual service.
|
||||
// +optional
|
||||
Spec v1alpha3.VirtualServiceSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// StrategyStatus defines the observed state of Strategy
|
||||
type StrategyStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
|
||||
// The latest available observations of an object's current state.
|
||||
// +optional
|
||||
Conditions []StrategyCondition
|
||||
|
||||
// Represents time when the strategy was acknowledged by the controller.
|
||||
// It is represented in RFC3339 form and is in UTC.
|
||||
// +optional
|
||||
StartTime *metav1.Time
|
||||
|
||||
// Represents time when the strategy was completed.
|
||||
// It is represented in RFC3339 form and is in UTC.
|
||||
// +optional
|
||||
CompletionTime *metav1.Time
|
||||
}
|
||||
|
||||
type StrategyConditionType string
|
||||
|
||||
// These are valid conditions of a strategy.
|
||||
const (
|
||||
// StrategyComplete means the strategy has been delivered to istio.
|
||||
StrategyComplete StrategyConditionType = "Complete"
|
||||
|
||||
// StrategyFailed means the strategy has failed its delivery to istio.
|
||||
StrategyFailed StrategyConditionType = "Failed"
|
||||
)
|
||||
|
||||
// StrategyCondition describes current state of a strategy.
|
||||
type StrategyCondition struct {
|
||||
// Type of strategy condition, Complete or Failed.
|
||||
Type StrategyConditionType
|
||||
|
||||
// Status of the condition, one of True, False, Unknown
|
||||
Status apiextensions.ConditionStatus
|
||||
|
||||
// Last time the condition was checked.
|
||||
// +optional
|
||||
LastProbeTime metav1.Time
|
||||
|
||||
// Last time the condition transit from one status to another
|
||||
// +optional
|
||||
LastTransitionTime metav1.Time
|
||||
|
||||
// reason for the condition's last transition
|
||||
Reason string
|
||||
|
||||
// Human readable message indicating details about last transition.
|
||||
// +optinal
|
||||
Message string
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// Strategy is the Schema for the strategies API
|
||||
// +k8s:openapi-gen=true
|
||||
type Strategy struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec StrategySpec `json:"spec,omitempty"`
|
||||
Status StrategyStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// StrategyList contains a list of Strategy
|
||||
type StrategyList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Strategy `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Strategy{}, &StrategyList{})
|
||||
}
|
||||
88
pkg/apis/servicemesh/v1alpha2/strategy_types_test.go
Normal file
88
pkg/apis/servicemesh/v1alpha2/strategy_types_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"io/ioutil"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
"golang.org/x/net/context"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func TestStorageStrategy(t *testing.T) {
|
||||
key := types.NamespacedName{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
}
|
||||
created := &Strategy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: StrategySpec{
|
||||
Template: VirtualServiceTemplateSpec{
|
||||
Spec: v1alpha3.VirtualServiceSpec{
|
||||
Hosts: []string{
|
||||
"details",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
g := gomega.NewGomegaWithT(t)
|
||||
|
||||
// Test Create
|
||||
fetched := &Strategy{}
|
||||
g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred())
|
||||
|
||||
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
|
||||
g.Expect(fetched).To(gomega.Equal(created))
|
||||
|
||||
// Test Updating the Labels
|
||||
updated := fetched.DeepCopy()
|
||||
updated.Labels = map[string]string{"hello": "world"}
|
||||
g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred())
|
||||
|
||||
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
|
||||
g.Expect(fetched).To(gomega.Equal(updated))
|
||||
|
||||
// Test Delete
|
||||
g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred())
|
||||
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
func TestStrategyRead(t *testing.T) {
|
||||
|
||||
//g := gomega.NewGomegaWithT(t)
|
||||
|
||||
var str []byte
|
||||
file, err := ioutil.ReadFile("/Users/zry/go/src/kubesphere.io/kubesphere/config/samples/servicemesh_v1alpha2_strategy.yaml")
|
||||
if err == nil {
|
||||
obj, _, _ := scheme.Codecs.UniversalDeserializer().Decode(file, nil, &Strategy{})
|
||||
switch obj.(type) {
|
||||
case *Strategy:
|
||||
str, err = json.Marshal(obj)
|
||||
t.Logf("Read strategy %s", str)
|
||||
}
|
||||
}
|
||||
}
|
||||
55
pkg/apis/servicemesh/v1alpha2/v1alpha2_suite_test.go
Normal file
55
pkg/apis/servicemesh/v1alpha2/v1alpha2_suite_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
)
|
||||
|
||||
var cfg *rest.Config
|
||||
var c client.Client
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
t := &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
|
||||
}
|
||||
|
||||
err := SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if cfg, err = t.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
t.Stop()
|
||||
os.Exit(code)
|
||||
}
|
||||
176
pkg/apis/servicemesh/v1alpha2/zz_generated.deepcopy.go
Normal file
176
pkg/apis/servicemesh/v1alpha2/zz_generated.deepcopy.go
Normal file
@@ -0,0 +1,176 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Strategy) DeepCopyInto(out *Strategy) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Strategy.
|
||||
func (in *Strategy) DeepCopy() *Strategy {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Strategy)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Strategy) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StrategyCondition) DeepCopyInto(out *StrategyCondition) {
|
||||
*out = *in
|
||||
in.LastProbeTime.DeepCopyInto(&out.LastProbeTime)
|
||||
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyCondition.
|
||||
func (in *StrategyCondition) DeepCopy() *StrategyCondition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StrategyCondition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StrategyList) DeepCopyInto(out *StrategyList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.ListMeta = in.ListMeta
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Strategy, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyList.
|
||||
func (in *StrategyList) DeepCopy() *StrategyList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StrategyList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *StrategyList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StrategySpec) DeepCopyInto(out *StrategySpec) {
|
||||
*out = *in
|
||||
if in.Selector != nil {
|
||||
in, out := &in.Selector, &out.Selector
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Template.DeepCopyInto(&out.Template)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategySpec.
|
||||
func (in *StrategySpec) DeepCopy() *StrategySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StrategySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StrategyStatus) DeepCopyInto(out *StrategyStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]StrategyCondition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.StartTime != nil {
|
||||
in, out := &in.StartTime, &out.StartTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
if in.CompletionTime != nil {
|
||||
in, out := &in.CompletionTime, &out.CompletionTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyStatus.
|
||||
func (in *StrategyStatus) DeepCopy() *StrategyStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StrategyStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VirtualServiceTemplateSpec) DeepCopyInto(out *VirtualServiceTemplateSpec) {
|
||||
*out = *in
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceTemplateSpec.
|
||||
func (in *VirtualServiceTemplateSpec) DeepCopy() *VirtualServiceTemplateSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VirtualServiceTemplateSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -66,6 +66,10 @@ func CreateUser(req *restful.Request, resp *restful.Response) {
|
||||
err = iam.CreateUser(user)
|
||||
|
||||
if err != nil {
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) {
|
||||
resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
@@ -180,7 +184,7 @@ func CurrentUserDetail(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
if err != nil {
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
|
||||
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
}
|
||||
@@ -228,6 +232,12 @@ func NamespacesListHandler(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
func UserDetail(req *restful.Request, resp *restful.Response) {
|
||||
username := req.PathParameter("name")
|
||||
usernameFromHeader := req.HeaderParameter(constants.UserNameHeader)
|
||||
|
||||
if username == usernameFromHeader {
|
||||
CurrentUserDetail(req, resp)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
apierror "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/errors"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
@@ -64,20 +62,6 @@ func WorkspaceRoles(req *restful.Request, resp *restful.Response) {
|
||||
resp.WriteAsJson(roles)
|
||||
}
|
||||
|
||||
func WorkspaceMemberQuery(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
keyword := req.QueryParameter("keyword")
|
||||
|
||||
users, err := workspaces.GetWorkspaceMembers(workspace, keyword)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(users)
|
||||
}
|
||||
|
||||
func WorkspaceMemberDetail(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("name")
|
||||
username := req.PathParameter("member")
|
||||
@@ -559,26 +543,10 @@ func DevopsRulesHandler(req *restful.Request, resp *restful.Response) {
|
||||
}
|
||||
|
||||
func NamespacesRulesHandler(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
//workspaceName := req.PathParameter("workspace")
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
namespaceName := req.PathParameter("namespace")
|
||||
|
||||
namespace, err := iam.GetNamespace(namespaceName)
|
||||
|
||||
if err != nil {
|
||||
if apierror.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("permission undefined")))
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if namespace.Labels == nil || namespace.Labels["kubesphere.io/workspace"] != workspaceName {
|
||||
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(fmt.Errorf("permission undefined")))
|
||||
return
|
||||
}
|
||||
|
||||
clusterRoles, err := iam.GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
|
||||
58
pkg/apiserver/resources/application.go
Normal file
58
pkg/apiserver/resources/application.go
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package resources
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"kubesphere.io/kubesphere/pkg/errors"
|
||||
"kubesphere.io/kubesphere/pkg/models/applications"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ApplicationHandler(req *restful.Request, resp *restful.Response) {
|
||||
limit, offset := params.ParsePaging(req)
|
||||
clusterId := req.QueryParameter("cluster_id")
|
||||
runtimeId := req.QueryParameter("runtime_id")
|
||||
conditions, err := params.ParseConditions(req)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(clusterId) > 0 {
|
||||
app, err := applications.GetApp(clusterId)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
resp.WriteEntity(app)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := applications.ListApplication(runtimeId, conditions, limit, offset)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(result)
|
||||
|
||||
}
|
||||
43
pkg/apiserver/servicemesh/metrics/handlers.go
Normal file
43
pkg/apiserver/servicemesh/metrics/handlers.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/kiali/kiali/handlers"
|
||||
)
|
||||
|
||||
// Get app metrics
|
||||
func GetAppMetrics(request *restful.Request, response *restful.Response) {
|
||||
handlers.AppMetrics(response.ResponseWriter, request.Request)
|
||||
}
|
||||
|
||||
// Get workload metrics
|
||||
func GetWorkloadMetrics(request *restful.Request, response *restful.Response) {
|
||||
handlers.WorkloadMetrics(response.ResponseWriter, request.Request)
|
||||
}
|
||||
|
||||
// Get service metrics
|
||||
func GetServiceMetrics(request *restful.Request, response *restful.Response) {
|
||||
handlers.ServiceMetrics(response.ResponseWriter, request.Request)
|
||||
}
|
||||
|
||||
// Get namespace metrics
|
||||
func GetNamespaceMetrics(request *restful.Request, response *restful.Response) {
|
||||
handlers.NamespaceMetrics(response.ResponseWriter, request.Request)
|
||||
}
|
||||
|
||||
// Get service graph for namespace
|
||||
func GetNamespaceGraph(request *restful.Request, response *restful.Response) {
|
||||
namespace := request.PathParameter("namespace")
|
||||
|
||||
if len(namespace) > 0 {
|
||||
request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s", request.Request.URL.RawQuery, namespace)
|
||||
}
|
||||
|
||||
handlers.GraphNamespaces(response.ResponseWriter, request.Request)
|
||||
}
|
||||
|
||||
// Get service graph for namespaces
|
||||
func GetNamespacesGraph(request *restful.Request, response *restful.Response) {
|
||||
handlers.GraphNamespaces(response.ResponseWriter, request.Request)
|
||||
}
|
||||
@@ -39,18 +39,12 @@ const (
|
||||
DevopsOwner = "owner"
|
||||
DevopsReporter = "reporter"
|
||||
|
||||
envDevopsAPIServer = "DEVOPS_API_SERVER"
|
||||
envAccountAPIServer = "ACCOUNT_API_SERVER"
|
||||
envDevopsProxyToken = "DEVOPS_PROXY_TOKEN"
|
||||
envOpenPitrixProxyToken = "OPENPITRIX_PROXY_TOKEN"
|
||||
|
||||
UserNameHeader = "X-Token-Username"
|
||||
)
|
||||
|
||||
var (
|
||||
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
|
||||
SystemWorkspace = "system-workspace"
|
||||
DevopsAPIServer = "ks-devops-apiserver.kubesphere-system.svc"
|
||||
AccountAPIServer = "ks-account.kubesphere-system.svc"
|
||||
DevopsAPIServer = "ks-devops.kubesphere-devops-system.svc"
|
||||
SystemNamespaces = []string{KubeSphereNamespace, OpenPitrixNamespace, KubeSystemNamespace}
|
||||
)
|
||||
|
||||
30
pkg/controller/add_strategy.go
Normal file
30
pkg/controller/add_strategy.go
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"kubesphere.io/kubesphere/pkg/controller/strategy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
|
||||
AddToManagerFuncs = append(AddToManagerFuncs, strategy.Add)
|
||||
|
||||
// Add application to manager functions
|
||||
//AddToManagerFuncs = append(AddToManagerFuncs, application.Add)
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
148
pkg/controller/strategy/strategy_controller.go
Normal file
148
pkg/controller/strategy/strategy_controller.go
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var log = logf.Log.WithName("controller")
|
||||
|
||||
// Add creates a new Strategy Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
||||
// and Start it when the Manager is Started.
|
||||
func Add(mgr manager.Manager) error {
|
||||
return add(mgr, newReconciler(mgr))
|
||||
}
|
||||
|
||||
// newReconciler returns a new reconcile.Reconciler
|
||||
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
|
||||
return &ReconcileStrategy{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
|
||||
}
|
||||
|
||||
// add adds a new Controller to mgr with r as the reconcile.Reconciler
|
||||
func add(mgr manager.Manager, r reconcile.Reconciler) error {
|
||||
// Create a new controller
|
||||
c, err := controller.New("strategy-controller", mgr, controller.Options{Reconciler: r})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch for changes to Strategy
|
||||
err = c.Watch(&source.Kind{Type: &servicemeshv1alpha2.Strategy{}}, &handler.EnqueueRequestForObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.Watch(&source.Kind{Type: &v1alpha3.VirtualService{}}, &handler.EnqueueRequestForObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(user): Modify this to be the types you create
|
||||
// Watch a VirtualService created by Strategy
|
||||
err = c.Watch(&source.Kind{Type: &v1alpha3.VirtualService{}}, &handler.EnqueueRequestForOwner{
|
||||
IsController: true,
|
||||
OwnerType: &servicemeshv1alpha2.Strategy{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ reconcile.Reconciler = &ReconcileStrategy{}
|
||||
|
||||
// ReconcileStrategy reconciles a Strategy object
|
||||
type ReconcileStrategy struct {
|
||||
client.Client
|
||||
scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// Reconcile reads that state of the cluster for a Strategy object and makes changes based on the state read
|
||||
// and what is in the Strategy.Spec
|
||||
// a Deployment as an example
|
||||
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
|
||||
// +kubebuilder:rbac:groups=networking.istio.io,resources=virtualservices,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=networking.istio.io,resources=virtualservices/status,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=servicemesh.kubesphere.io,resources=strategies,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=servicemesh.kubesphere.io,resources=strategies/status,verbs=get;update;patch
|
||||
func (r *ReconcileStrategy) Reconcile(request reconcile.Request) (reconcile.Result, error) {
|
||||
// Fetch the Strategy instance
|
||||
strategy := &servicemeshv1alpha2.Strategy{}
|
||||
err := r.Get(context.TODO(), request.NamespacedName, strategy)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
// Object not found, return. Created objects are automatically garbage collected.
|
||||
// For additional cleanup logic use finalizers.
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
// Error reading the object - requeue the request.
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Define VirtualService to be created
|
||||
vs := &v1alpha3.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: strategy.Name + "-virtualservice",
|
||||
Namespace: strategy.Namespace,
|
||||
Labels: strategy.Spec.Selector.MatchLabels,
|
||||
},
|
||||
Spec: strategy.Spec.Template.Spec,
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(strategy, vs, r.scheme); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Check if the VirtualService already exists
|
||||
found := &v1alpha3.VirtualService{}
|
||||
err = r.Get(context.TODO(), types.NamespacedName{Name: vs.Name, Namespace: vs.Namespace}, found)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
log.Info("Creating VirtualService", "namespace", vs.Namespace, "name", vs.Name)
|
||||
err = r.Create(context.TODO(), vs)
|
||||
return reconcile.Result{}, err
|
||||
} else if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Update the found object and write the result back if there are any changes
|
||||
if !reflect.DeepEqual(vs.Spec, found.Spec) {
|
||||
found.Spec = vs.Spec
|
||||
log.Info("Updating VirtualService", "namespace", vs.Namespace, "name", vs.Name)
|
||||
err = r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
75
pkg/controller/strategy/strategy_controller_suite_test.go
Normal file
75
pkg/controller/strategy/strategy_controller_suite_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategy
|
||||
|
||||
import (
|
||||
stdlog "log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"kubesphere.io/kubesphere/pkg/apis"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var cfg *rest.Config
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
t := &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
|
||||
}
|
||||
apis.AddToScheme(scheme.Scheme)
|
||||
|
||||
var err error
|
||||
if cfg, err = t.Start(); err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
t.Stop()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
|
||||
// writes the request to requests after Reconcile is finished.
|
||||
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
|
||||
requests := make(chan reconcile.Request)
|
||||
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
|
||||
result, err := inner.Reconcile(req)
|
||||
requests <- req
|
||||
return result, err
|
||||
})
|
||||
return fn, requests
|
||||
}
|
||||
|
||||
// StartTestManager adds recFn
|
||||
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
|
||||
stop := make(chan struct{})
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
|
||||
}()
|
||||
return stop, wg
|
||||
}
|
||||
143
pkg/controller/strategy/strategy_controller_test.go
Normal file
143
pkg/controller/strategy/strategy_controller_test.go
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright 2019 The KubeSphere authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategy
|
||||
|
||||
import (
|
||||
"github.com/knative/pkg/apis/istio/common/v1alpha1"
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
"golang.org/x/net/context"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var c client.Client
|
||||
|
||||
var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
|
||||
var depKey = types.NamespacedName{Name: "foo-virtualservice", Namespace: "default"}
|
||||
|
||||
const timeout = time.Second * 5
|
||||
|
||||
func TestReconcile(t *testing.T) {
|
||||
g := gomega.NewGomegaWithT(t)
|
||||
instance := &servicemeshv1alpha2.Strategy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: servicemeshv1alpha2.StrategySpec{
|
||||
Type: servicemeshv1alpha2.CanaryType,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"type": "Canary",
|
||||
},
|
||||
},
|
||||
Template: servicemeshv1alpha2.VirtualServiceTemplateSpec{
|
||||
Spec: v1alpha3.VirtualServiceSpec{
|
||||
Hosts: []string{"details"},
|
||||
Gateways: []string{"default"},
|
||||
Http: []v1alpha3.HTTPRoute{
|
||||
{
|
||||
Match: []v1alpha3.HTTPMatchRequest{
|
||||
{
|
||||
Method: &v1alpha1.StringMatch{
|
||||
Exact: "POST",
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: []v1alpha3.DestinationWeight{
|
||||
{
|
||||
Destination: v1alpha3.Destination{
|
||||
Host: "details",
|
||||
Subset: "v1",
|
||||
},
|
||||
Weight: 60,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Route: []v1alpha3.DestinationWeight{
|
||||
{
|
||||
Destination: v1alpha3.Destination{
|
||||
Host: "details",
|
||||
Subset: "v2",
|
||||
},
|
||||
Weight: 40,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a
|
||||
// channel when it is finished.
|
||||
mgr, err := manager.New(cfg, manager.Options{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
c = mgr.GetClient()
|
||||
|
||||
recFn, requests := SetupTestReconcile(newReconciler(mgr))
|
||||
g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred())
|
||||
|
||||
stopMgr, mgrStopped := StartTestManager(mgr, g)
|
||||
|
||||
defer func() {
|
||||
close(stopMgr)
|
||||
mgrStopped.Wait()
|
||||
}()
|
||||
|
||||
// Create the Strategy object and expect the Reconcile and Deployment to be created
|
||||
err = c.Create(context.TODO(), instance)
|
||||
// The instance object may not be a valid object because it might be missing some required fields.
|
||||
// Please modify the instance object by adding required fields and then remove the following if statement.
|
||||
if apierrors.IsInvalid(err) {
|
||||
t.Logf("failed to create object, got an invalid object error: %v", err)
|
||||
return
|
||||
}
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
defer c.Delete(context.TODO(), instance)
|
||||
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
|
||||
|
||||
vs := &v1alpha3.VirtualService{}
|
||||
g.Eventually(func() error { return c.Get(context.TODO(), depKey, vs) }, timeout).
|
||||
Should(gomega.Succeed())
|
||||
|
||||
if str, err := json.Marshal(vs); err == nil {
|
||||
t.Logf("Created virtual service %s\n", str)
|
||||
}
|
||||
|
||||
// Delete the Deployment and expect Reconcile to be called for Deployment deletion
|
||||
g.Expect(c.Delete(context.TODO(), vs)).NotTo(gomega.HaveOccurred())
|
||||
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
|
||||
//g.Eventually(func() error { return c.Get(context.TODO(), depKey, vs) }, timeout).Should(gomega.Succeed())
|
||||
|
||||
// Manually delete Deployment since GC isn't enabled in the test control plane
|
||||
g.Eventually(func() error { return c.Delete(context.TODO(), vs) }, timeout).
|
||||
Should(gomega.MatchError("virtualservices.networking.istio.io \"foo-virtualservice\" not found"))
|
||||
|
||||
}
|
||||
537
pkg/models/applications/applications.go
Normal file
537
pkg/models/applications/applications.go
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package applications
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"io/ioutil"
|
||||
v12 "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
OpenPitrixProxyToken string
|
||||
OpenPitrixServer string
|
||||
)
|
||||
|
||||
const (
|
||||
unknown = "-"
|
||||
deploySuffix = "-Deployment"
|
||||
daemonSuffix = "-DaemonSet"
|
||||
stateSuffix = "-StatefulSet"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
Name string `json:"name"`
|
||||
RepoName string `json:"repoName"`
|
||||
Runtime string `json:"namespace"`
|
||||
RuntimeId string `json:"runtime_id"`
|
||||
Version string `json:"version"`
|
||||
VersionId string `json:"version_id"`
|
||||
Status string `json:"status"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
App string `json:"app"`
|
||||
AppId string `json:"app_id"`
|
||||
Description string `json:"description,omitempty"`
|
||||
WorkLoads *workLoads `json:"workloads,omitempty"`
|
||||
Services []v1.Service `json:"services,omitempty"`
|
||||
Ingresses []v1beta1.Ingress `json:"ingresses,omitempty"`
|
||||
ClusterID string `json:"cluster_id"`
|
||||
}
|
||||
|
||||
type clusterRole struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type cluster struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"app_id"`
|
||||
VersionID string `json:"version_id"`
|
||||
Status string `json:"status"`
|
||||
UpdateTime time.Time `json:"status_time"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
RunTimeId string `json:"runtime_id"`
|
||||
Description string `json:"description"`
|
||||
ClusterRoleSets []clusterRole `json:"cluster_role_set"`
|
||||
}
|
||||
|
||||
type clusters struct {
|
||||
Total int `json:"total_count"`
|
||||
Clusters []cluster `json:"cluster_set"`
|
||||
}
|
||||
|
||||
type versionList struct {
|
||||
Total int `json:"total_count"`
|
||||
Versions []version `json:"app_version_set"`
|
||||
}
|
||||
|
||||
type version struct {
|
||||
Name string `json:"name"`
|
||||
VersionID string `json:"version_id"`
|
||||
}
|
||||
|
||||
type runtime struct {
|
||||
RuntimeID string `json:"runtime_id"`
|
||||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
type runtimeList struct {
|
||||
Total int `json:"total_count"`
|
||||
Runtimes []runtime `json:"runtime_set"`
|
||||
}
|
||||
|
||||
type app struct {
|
||||
AppId string `json:"app_id"`
|
||||
Name string `json:"name"`
|
||||
ChartName string `json:"chart_name"`
|
||||
RepoId string `json:"repo_id"`
|
||||
}
|
||||
|
||||
type repo struct {
|
||||
RepoId string `json:"repo_id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type workLoads struct {
|
||||
Deployments []v12.Deployment `json:"deployments,omitempty"`
|
||||
Statefulsets []v12.StatefulSet `json:"statefulsets,omitempty"`
|
||||
Daemonsets []v12.DaemonSet `json:"daemonsets,omitempty"`
|
||||
}
|
||||
|
||||
type appList struct {
|
||||
Total int `json:"total_count"`
|
||||
Apps []app `json:"app_set"`
|
||||
}
|
||||
|
||||
type repoList struct {
|
||||
Total int `json:"total_count"`
|
||||
Repos []repo `json:"repo_set"`
|
||||
}
|
||||
|
||||
func GetAppInfo(appId string) (string, string, string, error) {
|
||||
url := fmt.Sprintf("%s/v1/apps?app_id=%s", OpenPitrixServer, appId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
var apps appList
|
||||
err = json.Unmarshal(resp, &apps)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
if len(apps.Apps) == 0 {
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
return apps.Apps[0].ChartName, apps.Apps[0].RepoId, apps.Apps[0].AppId, nil
|
||||
}
|
||||
|
||||
func GetRepo(repoId string) (string, error) {
|
||||
url := fmt.Sprintf("%s/v1/repos?repo_id=%s", OpenPitrixServer, repoId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var repos repoList
|
||||
err = json.Unmarshal(resp, &repos)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(repos.Repos) == 0 {
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
return repos.Repos[0].Name, nil
|
||||
}
|
||||
|
||||
func GetVersion(versionId string) (string, error) {
|
||||
versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", OpenPitrixServer, versionId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var versions versionList
|
||||
err = json.Unmarshal(resp, &versions)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(versions.Versions) == 0 {
|
||||
return unknown, nil
|
||||
}
|
||||
return versions.Versions[0].Name, nil
|
||||
}
|
||||
|
||||
func GetRuntime(runtimeId string) (string, error) {
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", OpenPitrixServer, runtimeId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var runtimes runtimeList
|
||||
err = json.Unmarshal(resp, &runtimes)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(runtimes.Runtimes) == 0 {
|
||||
return unknown, nil
|
||||
}
|
||||
|
||||
return runtimes.Runtimes[0].Zone, nil
|
||||
}
|
||||
|
||||
func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, error) {
|
||||
|
||||
var works workLoads
|
||||
for _, clusterRole := range clusterRoles {
|
||||
workLoadName := clusterRole.Role
|
||||
if len(workLoadName) > 0 {
|
||||
if strings.HasSuffix(workLoadName, deploySuffix) {
|
||||
name := strings.Split(workLoadName, deploySuffix)[0]
|
||||
|
||||
item, err := informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
works.Deployments = append(works.Deployments, *item)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(workLoadName, daemonSuffix) {
|
||||
name := strings.Split(workLoadName, daemonSuffix)[0]
|
||||
item, err := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
works.Daemonsets = append(works.Daemonsets, *item)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(workLoadName, stateSuffix) {
|
||||
name := strings.Split(workLoadName, stateSuffix)[0]
|
||||
item, err := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
works.Statefulsets = append(works.Statefulsets, *item)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return &works, nil
|
||||
}
|
||||
|
||||
func getLabels(namespace string, workloads *workLoads) *[]map[string]string {
|
||||
k8sClient := k8s.Client()
|
||||
|
||||
var workloadLables []map[string]string
|
||||
if workloads == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, workload := range workloads.Deployments {
|
||||
deploy, err := k8sClient.AppsV1().Deployments(namespace).Get(workload.Name, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
workloadLables = append(workloadLables, deploy.Labels)
|
||||
}
|
||||
|
||||
for _, workload := range workloads.Daemonsets {
|
||||
daemonset, err := k8sClient.AppsV1().DaemonSets(namespace).Get(workload.Name, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
workloadLables = append(workloadLables, daemonset.Labels)
|
||||
}
|
||||
|
||||
for _, workload := range workloads.Statefulsets {
|
||||
statefulset, err := k8sClient.AppsV1().StatefulSets(namespace).Get(workload.Name, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
workloadLables = append(workloadLables, statefulset.Labels)
|
||||
}
|
||||
|
||||
return &workloadLables
|
||||
}
|
||||
|
||||
func isExist(svcs []v1.Service, svc v1.Service) bool {
|
||||
for _, item := range svcs {
|
||||
if item.Name == svc.Name && item.Namespace == svc.Namespace {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service {
|
||||
if len(*workLoadLabels) == 0 {
|
||||
return nil
|
||||
}
|
||||
k8sClient := k8s.Client()
|
||||
var services []v1.Service
|
||||
for _, label := range *workLoadLabels {
|
||||
labelSelector := labels.Set(label).AsSelector().String()
|
||||
svcs, err := k8sClient.CoreV1().Services(namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil {
|
||||
glog.Errorf("get app's svc failed, reason: %v", err)
|
||||
}
|
||||
for _, item := range svcs.Items {
|
||||
if !isExist(services, item) {
|
||||
services = append(services, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return services
|
||||
}
|
||||
|
||||
func getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
|
||||
if services == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ings []v1beta1.Ingress
|
||||
for _, svc := range services {
|
||||
result, err := resources.ListNamespaceResource(namespace, "ingress", ¶ms.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.Error(result)
|
||||
for _, i := range result.Items {
|
||||
ingress := i.(*v1beta1.Ingress)
|
||||
|
||||
exist := false
|
||||
var tmpRules []v1beta1.IngressRule
|
||||
for _, rule := range ingress.Spec.Rules {
|
||||
for _, p := range rule.HTTP.Paths {
|
||||
if p.Backend.ServiceName == svc.Name {
|
||||
exist = true
|
||||
tmpRules = append(tmpRules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if exist {
|
||||
ing := v1beta1.Ingress{}
|
||||
ing.Name = ingress.Name
|
||||
ing.Spec.Rules = tmpRules
|
||||
ings = append(ings, ing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ings
|
||||
}
|
||||
|
||||
func ListApplication(runtimeId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
|
||||
if strings.HasSuffix(OpenPitrixServer, "/") {
|
||||
OpenPitrixServer = strings.TrimSuffix(OpenPitrixServer, "/")
|
||||
}
|
||||
|
||||
defaultStatus := "status=active&status=stopped&status=pending&status=ceased"
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", OpenPitrixServer, strconv.Itoa(limit), strconv.Itoa(offset))
|
||||
|
||||
if len(conditions.Fuzzy["name"]) > 0 {
|
||||
url = fmt.Sprintf("%s&search_word=%s", url, conditions.Fuzzy["name"])
|
||||
}
|
||||
|
||||
if len(conditions.Match["status"]) > 0 {
|
||||
url = fmt.Sprintf("%s&status=%s", url, conditions.Match["status"])
|
||||
} else {
|
||||
url = fmt.Sprintf("%s&%s", url, defaultStatus)
|
||||
}
|
||||
|
||||
if len(runtimeId) > 0 {
|
||||
url = fmt.Sprintf("%s&runtime_id=%s", url, runtimeId)
|
||||
}
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Errorf("request %s failed, reason: %s", url, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clusterList clusters
|
||||
err = json.Unmarshal(resp, &clusterList)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := models.PageableResponse{TotalCount: clusterList.Total}
|
||||
result.Items = make([]interface{}, 0)
|
||||
for _, item := range clusterList.Clusters {
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
runtimeInfo, _ := GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, _, appId, _ := GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
result.Items = append(result.Items, app)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func GetApp(clusterId string) (*Application, error) {
|
||||
if strings.HasSuffix(OpenPitrixServer, "/") {
|
||||
OpenPitrixServer = strings.TrimSuffix(OpenPitrixServer, "/")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", OpenPitrixServer, clusterId)
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clusterList clusters
|
||||
err = json.Unmarshal(resp, &clusterList)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(clusterList.Clusters) == 0 {
|
||||
return nil, fmt.Errorf("NotFound, clusterId:%s", clusterId)
|
||||
}
|
||||
|
||||
item := clusterList.Clusters[0]
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.CreateTime = item.CreateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
|
||||
runtimeInfo, _ := GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, repoId, appId, _ := GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
app.RepoName, _ = GetRepo(repoId)
|
||||
|
||||
workloads, err := GetWorkLoads(app.Runtime, item.ClusterRoleSets)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
app.WorkLoads = workloads
|
||||
workloadLabels := getLabels(app.Runtime, app.WorkLoads)
|
||||
app.Services = getSvcs(app.Runtime, workloadLabels)
|
||||
app.Ingresses = getIng(app.Runtime, app.Services)
|
||||
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
func makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
var req *http.Request
|
||||
|
||||
var err error
|
||||
if method == "GET" {
|
||||
req, err = http.NewRequest(method, url, nil)
|
||||
} else {
|
||||
req, err = http.NewRequest(method, url, strings.NewReader(data))
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", OpenPitrixProxyToken)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Request to %s failed, method: %s, reason: %s ", url, method, err)
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
err = fmt.Errorf(string(body))
|
||||
}
|
||||
return body, err
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"io/ioutil"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
@@ -174,7 +175,7 @@ func GetRoles(username string, namespace string) ([]*v1.Role, error) {
|
||||
func GetClusterRoles(username string) ([]*v1.ClusterRole, error) {
|
||||
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
|
||||
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
|
||||
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
|
||||
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.SelectorFromSet(labels.Set{"": ""}))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -204,7 +205,7 @@ func GetClusterRoles(username string) ([]*v1.ClusterRole, error) {
|
||||
roles = append(roles, role)
|
||||
break
|
||||
} else if apierrors.IsNotFound(err) {
|
||||
log.Println(err)
|
||||
glog.Warningln(err)
|
||||
break
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -572,8 +573,10 @@ func GetRoleSimpleRules(roles []*v1.Role, namespace string) (map[string][]models
|
||||
return rulesMapping, nil
|
||||
}
|
||||
|
||||
//
|
||||
func CreateClusterRoleBinding(username string, clusterRoleName string) error {
|
||||
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
|
||||
|
||||
_, err := clusterRoleLister.Get(clusterRoleName)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -18,19 +18,13 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
|
||||
"k8s.io/api/rbac/v1"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
kserr "kubesphere.io/kubesphere/pkg/errors"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
@@ -75,27 +69,18 @@ func GetUsers(names []string) ([]models.User, error) {
|
||||
return make([]models.User, 0), nil
|
||||
}
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?name=%s", constants.AccountAPIServer, strings.Join(names, ",")))
|
||||
conn, err := ldap.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &users)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, name := range names {
|
||||
user, err := UserDetail(name, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, *user)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
@@ -103,32 +88,19 @@ func GetUsers(names []string) ([]models.User, error) {
|
||||
|
||||
func GetUser(name string) (*models.User, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users/%s", constants.AccountAPIServer, name))
|
||||
conn, err := ldap.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
user, err := UserDetail(name, conn)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
var user models.User
|
||||
|
||||
err = json.Unmarshal(data, &user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) {
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/redis"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -42,27 +41,18 @@ import (
|
||||
jwtutils "kubesphere.io/kubesphere/pkg/utils/jwt"
|
||||
)
|
||||
|
||||
const (
|
||||
envAdminEmail = "ADMIN_EMAIL"
|
||||
envAdminPWD = "ADMIN_PWD"
|
||||
)
|
||||
|
||||
var (
|
||||
counter Counter
|
||||
AdminEmail = "admin@kubesphere.io"
|
||||
AdminPWD = "passw0rd"
|
||||
counter Counter
|
||||
adminEmail string
|
||||
adminPassword string
|
||||
tokenExpireTime time.Duration
|
||||
)
|
||||
|
||||
func init() {
|
||||
if env := os.Getenv(envAdminEmail); env != "" {
|
||||
AdminEmail = env
|
||||
}
|
||||
if env := os.Getenv(envAdminPWD); env != "" {
|
||||
AdminPWD = env
|
||||
}
|
||||
}
|
||||
func Init(email, password string, t time.Duration) error {
|
||||
adminEmail = email
|
||||
adminPassword = password
|
||||
tokenExpireTime = t
|
||||
|
||||
func DatabaseInit() error {
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
@@ -96,13 +86,16 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
|
||||
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
|
||||
err = createGroupsBaseDN(conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GroupBaseDN %s create failed: %s\n", ldapclient.GroupSearchBase, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("GroupBaseDN %s not exist: %s\n", ldapclient.GroupSearchBase, err)
|
||||
return fmt.Errorf("iam database init failed: %s\n", err)
|
||||
}
|
||||
|
||||
if len(groups.Entries) == 0 {
|
||||
if groups == nil || len(groups.Entries) == 0 {
|
||||
_, err = CreateGroup(models.Group{Path: constants.SystemWorkspace, Name: constants.SystemWorkspace, Creator: constants.AdminUserName, Description: "system workspace"})
|
||||
|
||||
if err != nil {
|
||||
@@ -127,21 +120,24 @@ func checkAndCreateDefaultUser(conn ldap.Client) error {
|
||||
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
|
||||
err = createUserBaseDN(conn)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("UserBaseDN %s not exist: %s\n", ldapclient.UserSearchBase, err)
|
||||
}
|
||||
|
||||
if len(users.Entries) == 0 {
|
||||
err := CreateUser(models.User{Username: constants.AdminUserName, Email: AdminEmail, Password: AdminPWD, Description: "Administrator account that was always created by default."})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("admin create failed: %s\n", err)
|
||||
return fmt.Errorf("UserBaseDN %s create failed: %s\n", ldapclient.UserSearchBase, err)
|
||||
}
|
||||
}
|
||||
|
||||
counter = NewCounter(len(users.Entries))
|
||||
if err != nil {
|
||||
return fmt.Errorf("iam database init failed: %s\n", err)
|
||||
}
|
||||
|
||||
if users == nil || len(users.Entries) == 0 {
|
||||
counter = NewCounter(0)
|
||||
err := CreateUser(models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
|
||||
if err != nil {
|
||||
return fmt.Errorf("admin create failed: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
counter = NewCounter(len(users.Entries))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -200,8 +196,6 @@ func Login(username string, password string, ip string) (string, error) {
|
||||
email := result.Entries[0].GetAttributeValue("mail")
|
||||
dn := result.Entries[0].DN
|
||||
|
||||
user := models.User{Username: uid, Email: email}
|
||||
|
||||
// bind as the user to verify their password
|
||||
err = conn.Bind(dn, password)
|
||||
|
||||
@@ -209,25 +203,29 @@ func Login(username string, password string, ip string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ip != "" {
|
||||
redisClient := redis.Client()
|
||||
redisClient.RPush(fmt.Sprintf("kubesphere:users:%s:login-log", uid), fmt.Sprintf("%s,%s", time.Now().UTC().Format("2006-01-02T15:04:05Z"), ip))
|
||||
redisClient.LTrim(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -10, -1)
|
||||
}
|
||||
|
||||
claims := jwt.MapClaims{}
|
||||
|
||||
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
|
||||
claims["username"] = user.Username
|
||||
claims["email"] = user.Email
|
||||
claims["exp"] = time.Now().Add(tokenExpireTime).Unix()
|
||||
claims["username"] = uid
|
||||
claims["email"] = email
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
uToken, _ := token.SignedString(jwtutils.Secret)
|
||||
|
||||
loginLog(uid, ip)
|
||||
|
||||
return uToken, nil
|
||||
}
|
||||
|
||||
func loginLog(uid, ip string) {
|
||||
if ip != "" {
|
||||
redisClient := redis.Client()
|
||||
redisClient.RPush(fmt.Sprintf("kubesphere:users:%s:login-log", uid), fmt.Sprintf("%s,%s", time.Now().UTC().Format("2006-01-02T15:04:05Z"), ip))
|
||||
redisClient.LTrim(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -10, -1)
|
||||
}
|
||||
}
|
||||
|
||||
func UserList(limit int, offset int) (int, []models.User, error) {
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
@@ -668,7 +666,7 @@ func CreateUser(user models.User) error {
|
||||
}
|
||||
|
||||
if len(result.Entries) > 0 {
|
||||
return errors.New("username or email already exists")
|
||||
return ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
|
||||
}
|
||||
|
||||
maxUid, err := getMaxUid(conn)
|
||||
|
||||
@@ -732,7 +732,7 @@ func MonitorAllWorkspacesStatistics() *FormatedLevelMetric {
|
||||
wg.Add(4)
|
||||
|
||||
go func() {
|
||||
orgNums, errOrg := workspaces.Count()
|
||||
orgNums, errOrg := workspaces.WorkspaceCount()
|
||||
if errOrg != nil {
|
||||
glog.Errorln(errOrg.Error())
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ const (
|
||||
daemonsetsKey = "count/daemonsets.apps"
|
||||
deploymentsKey = "count/deployments.apps"
|
||||
ingressKey = "count/ingresses.extensions"
|
||||
rolesKey = "count/roles.rbac.authorization.k8s.io"
|
||||
clusterRolesKey = "count/cluster-role"
|
||||
servicesKey = "count/services"
|
||||
statefulsetsKey = "count/statefulsets.apps"
|
||||
persistentvolumeclaimsKey = "persistentvolumeclaims"
|
||||
@@ -47,18 +45,26 @@ const (
|
||||
|
||||
var (
|
||||
resourceMap = map[string]string{daemonsetsKey: resources.DaemonSets, deploymentsKey: resources.Deployments,
|
||||
ingressKey: resources.Ingresses, rolesKey: resources.Roles, servicesKey: resources.Services,
|
||||
ingressKey: resources.Ingresses, servicesKey: resources.Services,
|
||||
statefulsetsKey: resources.StatefulSets, persistentvolumeclaimsKey: resources.PersistentVolumeClaims, podsKey: resources.Pods,
|
||||
namespaceKey: resources.Namespaces, storageClassesKey: resources.StorageClasses, clusterRolesKey: resources.ClusterRoles,
|
||||
namespaceKey: resources.Namespaces, storageClassesKey: resources.StorageClasses,
|
||||
jobsKey: resources.Jobs, cronJobsKey: resources.CronJobs}
|
||||
)
|
||||
|
||||
func getUsage(namespace, resource string) (int, error) {
|
||||
list, err := resources.ListNamespaceResource(namespace, resource, ¶ms.Conditions{}, "", false, -1, 0)
|
||||
var result *models.PageableResponse
|
||||
var err error
|
||||
if resource == resources.Namespaces || resource == resources.StorageClasses {
|
||||
result, err = resources.ListClusterResource(resource, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
} else {
|
||||
result, err = resources.ListNamespaceResource(namespace, resource, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return list.TotalCount, nil
|
||||
|
||||
return result.TotalCount, nil
|
||||
}
|
||||
|
||||
func GetClusterQuotas() (*models.ResourceQuota, error) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
v12 "k8s.io/api/apps/v1"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"sort"
|
||||
@@ -30,16 +31,149 @@ import (
|
||||
type podSearcher struct {
|
||||
}
|
||||
|
||||
func podBelongTo(item *v1.Pod, kind string, name string) bool {
|
||||
|
||||
if strings.EqualFold(kind, "Deployment") {
|
||||
if podBelongToDeployment(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "ReplicaSet") {
|
||||
if podBelongToReplicaSet(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "DaemonSet") {
|
||||
if podBelongToDaemonSet(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "StatefulSet") {
|
||||
if podBelongToStatefulSet(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "Job") {
|
||||
if podBelongToJob(item, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func replicaSetBelongToDeployment(replicaSet *v12.ReplicaSet, name string) bool {
|
||||
for _, owner := range replicaSet.OwnerReferences {
|
||||
if owner.Kind == "Deployment" && owner.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToDaemonSet(item *v1.Pod, name string) bool {
|
||||
for _, owner := range item.OwnerReferences {
|
||||
if owner.Kind == "DaemonSet" && owner.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToJob(item *v1.Pod, name string) bool {
|
||||
for _, owner := range item.OwnerReferences {
|
||||
if owner.Kind == "Job" && owner.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToReplicaSet(item *v1.Pod, name string) bool {
|
||||
for _, owner := range item.OwnerReferences {
|
||||
if owner.Kind == "ReplicaSet" && owner.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToStatefulSet(item *v1.Pod, name string) bool {
|
||||
replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, r := range replicas {
|
||||
if replicaSetBelongToDeployment(r, name) {
|
||||
return podBelongToReplicaSet(item, r.Name)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToDeployment(item *v1.Pod, name string) bool {
|
||||
replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, r := range replicas {
|
||||
if replicaSetBelongToDeployment(r, name) {
|
||||
return podBelongToReplicaSet(item, r.Name)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBindPVC(item *v1.Pod, pvcName string) bool {
|
||||
for _, v := range item.Spec.Volumes {
|
||||
if v.VolumeSource.PersistentVolumeClaim != nil &&
|
||||
v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToService(item *v1.Pod, serviceName string) bool {
|
||||
service, err := informers.SharedInformerFactory().Core().V1().Services().Lister().Services(item.Namespace).Get(serviceName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for k, v := range service.Spec.Selector {
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
case "ownerKind":
|
||||
fallthrough
|
||||
case "ownerName":
|
||||
kind := match["ownerKind"]
|
||||
name := match["ownerName"]
|
||||
if !podBelongTo(item, kind, name) {
|
||||
return false
|
||||
}
|
||||
case "nodeName":
|
||||
if item.Spec.NodeName != v {
|
||||
return false
|
||||
}
|
||||
case "pvcName":
|
||||
if !podBindPVC(item, v) {
|
||||
return false
|
||||
}
|
||||
case "serviceName":
|
||||
if !podBelongToService(item, v) {
|
||||
return false
|
||||
}
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -131,6 +131,8 @@ func ListClusterResource(resource string, conditions *params.Conditions, orderBy
|
||||
|
||||
if searcher, ok := clusterResources[resource]; ok {
|
||||
result, err = searcher.search(conditions, orderBy, reverse)
|
||||
} else if searcher, ok := namespacedResources[resource]; ok {
|
||||
result, err = searcher.search("", conditions, orderBy, reverse)
|
||||
} else {
|
||||
return nil, fmt.Errorf("not support")
|
||||
}
|
||||
|
||||
7
pkg/models/servicemesh/metrics/handlers.go
Normal file
7
pkg/models/servicemesh/metrics/handlers.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package metrics
|
||||
|
||||
import "github.com/emicklei/go-restful"
|
||||
|
||||
func GetAppMetrics(request *restful.Request, response *restful.Response) {
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
|
||||
"net/http"
|
||||
|
||||
@@ -40,7 +41,6 @@ import (
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -56,7 +56,6 @@ import (
|
||||
|
||||
func UnBindDevopsProject(workspace string, devops string) error {
|
||||
db := mysql.Client()
|
||||
defer db.Close()
|
||||
return db.Delete(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
|
||||
}
|
||||
|
||||
@@ -306,7 +305,6 @@ func Namespaces(workspaceName string) ([]*core.Namespace, error) {
|
||||
|
||||
func BindingDevopsProject(workspace string, devops string) error {
|
||||
db := mysql.Client()
|
||||
defer db.Close()
|
||||
return db.Create(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
|
||||
}
|
||||
|
||||
@@ -332,27 +330,11 @@ func Delete(workspace *models.Workspace) error {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, workspace.Name), nil)
|
||||
err = iam.DeleteGroup(workspace.Name)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return kserr.Parse(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -401,34 +383,13 @@ func workspaceRoleRelease(workspace string) error {
|
||||
|
||||
func Create(workspace *models.Workspace) (*models.Workspace, error) {
|
||||
|
||||
data, err := json.Marshal(workspace)
|
||||
|
||||
group, err := iam.CreateGroup(workspace.Group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := http.Post(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups", constants.AccountAPIServer), restful.MIME_JSON, bytes.NewReader(data))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer result.Body.Close()
|
||||
data, err = ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
var created models.Workspace
|
||||
|
||||
err = json.Unmarshal(data, &created)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
created := models.Workspace{
|
||||
Group: *group,
|
||||
}
|
||||
|
||||
created.Members = make([]string, 0)
|
||||
@@ -446,78 +407,35 @@ func Create(workspace *models.Workspace) (*models.Workspace, error) {
|
||||
|
||||
func Edit(workspace *models.Workspace) (*models.Workspace, error) {
|
||||
|
||||
data, err := json.Marshal(workspace)
|
||||
group, err := iam.UpdateGroup(&workspace.Group)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, workspace.Name), bytes.NewReader(data))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
workspace.Group = *group
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err = ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
var edited models.Workspace
|
||||
|
||||
err = json.Unmarshal(data, &edited)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &edited, nil
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
func Detail(name string) (*models.Workspace, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, name))
|
||||
|
||||
conn, err := ldap.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
defer conn.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
var group models.Group
|
||||
|
||||
err = json.Unmarshal(data, &group)
|
||||
group, err := iam.GroupDetail(name, conn)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := mysql.Client()
|
||||
defer db.Close()
|
||||
|
||||
workspace, err := convertGroupToWorkspace(db, group)
|
||||
workspace, err := convertGroupToWorkspace(db, *group)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -570,45 +488,33 @@ func ListWorkspaceByUser(username string, keyword string) ([]*models.Workspace,
|
||||
|
||||
func fetch(names []string) ([]*models.Workspace, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups", constants.AccountAPIServer)
|
||||
|
||||
if names != nil {
|
||||
if len(names) == 0 {
|
||||
return make([]*models.Workspace, 0), nil
|
||||
} else {
|
||||
url = url + "?path=" + strings.Join(names, ",")
|
||||
if names != nil && len(names) == 0 {
|
||||
return make([]*models.Workspace, 0), nil
|
||||
}
|
||||
var groups []models.Group
|
||||
var err error
|
||||
if names == nil {
|
||||
groups, err = iam.ChildList("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
conn, err := ldap.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
for _, name := range names {
|
||||
group, err := iam.GroupDetail(name, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups = append(groups, *group)
|
||||
}
|
||||
}
|
||||
|
||||
result, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
var groups []models.Group
|
||||
|
||||
err = json.Unmarshal(data, &groups)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := mysql.Client()
|
||||
|
||||
defer db.Close()
|
||||
|
||||
workspaces := make([]*models.Workspace, 0)
|
||||
for _, group := range groups {
|
||||
workspace, err := convertGroupToWorkspace(db, group)
|
||||
@@ -624,7 +530,6 @@ func fetch(names []string) ([]*models.Workspace, error) {
|
||||
func ListDevopsProjectsByUser(username string, workspace string, keyword string, orderBy string, reverse bool, limit int, offset int) (int, []models.DevopsProject, error) {
|
||||
|
||||
db := mysql.Client()
|
||||
defer db.Close()
|
||||
|
||||
var workspaceDOPBindings []models.WorkspaceDPBinding
|
||||
|
||||
@@ -850,43 +755,6 @@ func Roles(workspace *models.Workspace) ([]*v1.ClusterRole, error) {
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
func GetWorkspaceMembers(workspace string, keyword string) ([]models.User, error) {
|
||||
|
||||
url := fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/workspaces/%s/members", constants.AccountAPIServer, workspace)
|
||||
|
||||
if keyword != "" {
|
||||
url = url + "?keyword=" + keyword
|
||||
}
|
||||
|
||||
result, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
var users []models.User
|
||||
|
||||
err = json.Unmarshal(data, &users)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
|
||||
}
|
||||
|
||||
func WorkspaceRoleInit(workspace *models.Workspace) error {
|
||||
k8sClient := k8s.Client()
|
||||
|
||||
@@ -1222,7 +1090,6 @@ func CreateWorkspaceRoleBinding(workspace *models.Workspace, username string, ro
|
||||
func GetDevOpsProjects(workspaceName string) ([]string, error) {
|
||||
|
||||
db := mysql.Client()
|
||||
defer db.Close()
|
||||
|
||||
var workspaceDOPBindings []models.WorkspaceDPBinding
|
||||
|
||||
@@ -1266,42 +1133,15 @@ func WorkspaceNamespaces(workspaceName string) ([]string, error) {
|
||||
return namespaces, nil
|
||||
}
|
||||
|
||||
func Count() (int, error) {
|
||||
func WorkspaceCount() (int, error) {
|
||||
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/count", constants.AccountAPIServer))
|
||||
workspaces, err := iam.ChildList("")
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return 0, kserr.Parse(data)
|
||||
}
|
||||
var count map[string]json.Number
|
||||
|
||||
err = json.Unmarshal(data, &count)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value := count["total_count"]
|
||||
|
||||
v, err := value.Int64()
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(v), nil
|
||||
return len(workspaces), nil
|
||||
}
|
||||
|
||||
func GetAllProjectNums() (int, error) {
|
||||
@@ -1315,7 +1155,6 @@ func GetAllProjectNums() (int, error) {
|
||||
|
||||
func GetAllDevOpsProjectsNums() (int, error) {
|
||||
db := mysql.Client()
|
||||
defer db.Close()
|
||||
|
||||
var count int
|
||||
if err := db.Model(&models.WorkspaceDPBinding{}).Count(&count).Error; err != nil {
|
||||
@@ -1325,35 +1164,11 @@ func GetAllDevOpsProjectsNums() (int, error) {
|
||||
}
|
||||
|
||||
func GetAllAccountNums() (int, error) {
|
||||
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users", constants.AccountAPIServer))
|
||||
totalCount, _, err := iam.UserList(1, 0)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if result.StatusCode > 200 {
|
||||
return 0, kserr.Parse(data)
|
||||
}
|
||||
var count map[string]json.Number
|
||||
|
||||
err = json.Unmarshal(data, &count)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value := count["total_count"]
|
||||
|
||||
v, err := value.Int64()
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(v), nil
|
||||
return totalCount, nil
|
||||
}
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
*/
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var SharedOptions = NewServerRunOptions()
|
||||
import "github.com/spf13/pflag"
|
||||
|
||||
type ServerRunOptions struct {
|
||||
// server bind address
|
||||
@@ -38,8 +34,6 @@ type ServerRunOptions struct {
|
||||
|
||||
// tls private key file
|
||||
TlsPrivateKey string
|
||||
|
||||
CommandLine *pflag.FlagSet
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
@@ -50,14 +44,16 @@ func NewServerRunOptions() *ServerRunOptions {
|
||||
SecurePort: 0,
|
||||
TlsCertFile: "",
|
||||
TlsPrivateKey: "",
|
||||
CommandLine: &pflag.FlagSet{},
|
||||
}
|
||||
|
||||
s.CommandLine.StringVar(&s.BindAddress, "bind-address", "0.0.0.0", "server bind address")
|
||||
s.CommandLine.IntVar(&s.InsecurePort, "insecure-port", 9090, "insecure port number")
|
||||
s.CommandLine.IntVar(&s.SecurePort, "secure-port", 0, "secure port number")
|
||||
s.CommandLine.StringVar(&s.TlsCertFile, "tls-cert-file", "", "tls cert file")
|
||||
s.CommandLine.StringVar(&s.TlsPrivateKey, "tls-private-key", "", "tls private key")
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
|
||||
fs.StringVar(&s.BindAddress, "bind-address", "0.0.0.0", "server bind address")
|
||||
fs.IntVar(&s.InsecurePort, "insecure-port", 9090, "insecure port number")
|
||||
fs.IntVar(&s.SecurePort, "secure-port", 0, "secure port number")
|
||||
fs.StringVar(&s.TlsCertFile, "tls-cert-file", "", "tls cert file")
|
||||
fs.StringVar(&s.TlsPrivateKey, "tls-private-key", "", "tls private key")
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/container/intsets"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,7 +34,7 @@ const (
|
||||
|
||||
func ParsePaging(req *restful.Request) (limit, offset int) {
|
||||
paging := req.QueryParameter(PagingParam)
|
||||
limit = intsets.MaxInt
|
||||
limit = 10
|
||||
offset = 0
|
||||
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(paging); len(groups) == 3 {
|
||||
limit, _ = strconv.Atoi(groups[1])
|
||||
|
||||
@@ -38,11 +38,11 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
prometheusAPIEndpoint string
|
||||
PrometheusAPIEndpoint string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&prometheusAPIEndpoint, "prometheus-endpoint", "http://prometheus-k8s.kubesphere-monitoring-system.svc:9090/api/v1/", "prometheus api endpoint")
|
||||
flag.StringVar(&PrometheusAPIEndpoint, "prometheus-endpoint", "http://prometheus-k8s.kubesphere-monitoring-system.svc:9090/api/v1/", "prometheus api endpoint")
|
||||
}
|
||||
|
||||
type MonitoringRequestParams struct {
|
||||
@@ -71,7 +71,7 @@ type MonitoringRequestParams struct {
|
||||
}
|
||||
|
||||
func SendMonitoringRequest(queryType string, params string) string {
|
||||
epurl := prometheusAPIEndpoint + queryType + params
|
||||
epurl := PrometheusAPIEndpoint + queryType + params
|
||||
|
||||
response, err := http.DefaultClient.Get(epurl)
|
||||
if err != nil {
|
||||
|
||||
7
pkg/utils/utils.go
Normal file
7
pkg/utils/utils.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package utils
|
||||
|
||||
func CheckError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,15 @@ import (
|
||||
"github.com/emicklei/go-restful-openapi"
|
||||
"github.com/go-openapi/spec"
|
||||
"io/ioutil"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/iam/install"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"log"
|
||||
// Install apis
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/metrics/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/monitoring/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/operations/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/resources/install"
|
||||
_ "kubesphere.io/kubesphere/pkg/apis/servicemesh/metrics/install"
|
||||
)
|
||||
|
||||
var output string
|
||||
|
||||
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
*.exe
|
||||
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
@@ -1,22 +0,0 @@
|
||||
# go-winio
|
||||
|
||||
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||
for using named pipes as a net transport.
|
||||
|
||||
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
||||
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
||||
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
||||
package.
|
||||
|
||||
Please see the LICENSE file for licensing information.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of
|
||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||
see the [Code of Conduct
|
||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||
questions or comments.
|
||||
|
||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
||||
for another named pipe implementation.
|
||||
5
vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
5
vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
||||
*.sublime-*
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
tags
|
||||
12
vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
12
vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
@@ -1,12 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- tip
|
||||
188
vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
188
vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
@@ -1,188 +0,0 @@
|
||||
# Purell
|
||||
|
||||
Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know...
|
||||
|
||||
Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc].
|
||||
|
||||
[](http://travis-ci.org/PuerkitoBio/purell)
|
||||
|
||||
## Install
|
||||
|
||||
`go get github.com/PuerkitoBio/purell`
|
||||
|
||||
## Changelog
|
||||
|
||||
* **v1.1.1** : Fix failing test due to Go1.12 changes (thanks to @ianlancetaylor).
|
||||
* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121).
|
||||
* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich).
|
||||
* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]).
|
||||
* **v0.2.0** : Add benchmarks, Attempt IDN support.
|
||||
* **v0.1.0** : Initial release.
|
||||
|
||||
## Examples
|
||||
|
||||
From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."):
|
||||
|
||||
```go
|
||||
package purell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func ExampleNormalizeURLString() {
|
||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
||||
}
|
||||
|
||||
func ExampleMustNormalizeURLString() {
|
||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
||||
FlagsUnsafeGreedy)
|
||||
fmt.Print(normalized)
|
||||
|
||||
// Output: http://somewebsite.com/Amazing%FA/url
|
||||
}
|
||||
|
||||
func ExampleNormalizeURL() {
|
||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
|
||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags:
|
||||
|
||||
```go
|
||||
const (
|
||||
// Safe normalizations
|
||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
||||
FlagLowercaseHost // http://HOST -> http://host
|
||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
||||
|
||||
// Usually safe normalizations
|
||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
||||
|
||||
// Unsafe normalizations
|
||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
||||
FlagForceHTTP // https://host -> http://host
|
||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
||||
|
||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
||||
// submitted by jehiah
|
||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
||||
|
||||
// Convenience set of safe normalizations
|
||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
||||
|
||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
||||
|
||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
||||
|
||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
||||
|
||||
// Convenience set of all available flags
|
||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
)
|
||||
```
|
||||
|
||||
For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set.
|
||||
|
||||
The [full godoc reference is available on gopkgdoc][godoc].
|
||||
|
||||
Some things to note:
|
||||
|
||||
* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it.
|
||||
|
||||
* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*):
|
||||
- %24 -> $
|
||||
- %26 -> &
|
||||
- %2B-%3B -> +,-./0123456789:;
|
||||
- %3D -> =
|
||||
- %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
- %5F -> _
|
||||
- %61-%7A -> abcdefghijklmnopqrstuvwxyz
|
||||
- %7E -> ~
|
||||
|
||||
|
||||
* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization).
|
||||
|
||||
* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell.
|
||||
|
||||
* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object.
|
||||
|
||||
### Safe vs Usually Safe vs Unsafe
|
||||
|
||||
Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between.
|
||||
|
||||
Consider the following URL:
|
||||
|
||||
`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
Normalizing with the `FlagsSafe` gives:
|
||||
|
||||
`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
With the `FlagsUsuallySafeGreedy`:
|
||||
|
||||
`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
And with `FlagsUnsafeGreedy`:
|
||||
|
||||
`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3`
|
||||
|
||||
## TODOs
|
||||
|
||||
* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`.
|
||||
|
||||
## Thanks / Contributions
|
||||
|
||||
@rogpeppe
|
||||
@jehiah
|
||||
@opennota
|
||||
@pchristopher1275
|
||||
@zenovich
|
||||
@beeker1121
|
||||
|
||||
## License
|
||||
|
||||
The [BSD 3-Clause license][bsd].
|
||||
|
||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
||||
[wiki]: http://en.wikipedia.org/wiki/URL_normalization
|
||||
[rfc]: http://tools.ietf.org/html/rfc3986#section-6
|
||||
[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell
|
||||
[pr5]: https://github.com/PuerkitoBio/purell/pull/5
|
||||
[iss7]: https://github.com/PuerkitoBio/purell/issues/7
|
||||
15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml
generated
vendored
15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml
generated
vendored
@@ -1,15 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- tip
|
||||
|
||||
install:
|
||||
- go build .
|
||||
|
||||
script:
|
||||
- go test -v
|
||||
16
vendor/github.com/PuerkitoBio/urlesc/README.md
generated
vendored
16
vendor/github.com/PuerkitoBio/urlesc/README.md
generated
vendored
@@ -1,16 +0,0 @@
|
||||
urlesc [](https://travis-ci.org/PuerkitoBio/urlesc) [](http://godoc.org/github.com/PuerkitoBio/urlesc)
|
||||
======
|
||||
|
||||
Package urlesc implements query escaping as per RFC 3986.
|
||||
|
||||
It contains some parts of the net/url package, modified so as to allow
|
||||
some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)).
|
||||
|
||||
## Install
|
||||
|
||||
go get github.com/PuerkitoBio/urlesc
|
||||
|
||||
## License
|
||||
|
||||
Go license (BSD-3-Clause)
|
||||
|
||||
2
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
2
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
@@ -1,2 +0,0 @@
|
||||
logrus
|
||||
vendor
|
||||
52
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
52
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
@@ -1,52 +0,0 @@
|
||||
language: go
|
||||
go_import_path: github.com/sirupsen/logrus
|
||||
env:
|
||||
- GOMAXPROCS=4 GORACE=halt_on_error=1
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.10.x
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=on
|
||||
install:
|
||||
- go mod download
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=off
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
- go: 1.10.x
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v -tags appengine ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=on
|
||||
install:
|
||||
- go mod download
|
||||
script:
|
||||
- go test -race -v -tags appengine ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=off
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v -tags appengine ./...
|
||||
190
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
190
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
@@ -1,190 +0,0 @@
|
||||
# 1.4.0
|
||||
This new release introduces:
|
||||
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
||||
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911)
|
||||
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
||||
|
||||
Fixes:
|
||||
* Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
|
||||
* Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
|
||||
* Fix infinite recursion on unknown `Level.String()` (#907)
|
||||
* Fix race condition in `getCaller` (#916).
|
||||
|
||||
|
||||
# 1.3.0
|
||||
This new release introduces:
|
||||
* Log, Logf, Logln functions for Logger and Entry that take a Level
|
||||
|
||||
Fixes:
|
||||
* Building prometheus node_exporter on AIX (#840)
|
||||
* Race condition in TextFormatter (#468)
|
||||
* Travis CI import path (#868)
|
||||
* Remove coloured output on Windows (#862)
|
||||
* Pointer to func as field in JSONFormatter (#870)
|
||||
* Properly marshal Levels (#873)
|
||||
|
||||
# 1.2.0
|
||||
This new release introduces:
|
||||
* A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
|
||||
* A new trace level named `Trace` whose level is below `Debug`
|
||||
* A configurable exit function to be called upon a Fatal trace
|
||||
* The `Level` object now implements `encoding.TextUnmarshaler` interface
|
||||
|
||||
# 1.1.1
|
||||
This is a bug fix release.
|
||||
* fix the build break on Solaris
|
||||
* don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
|
||||
|
||||
# 1.1.0
|
||||
This new release introduces:
|
||||
* several fixes:
|
||||
* a fix for a race condition on entry formatting
|
||||
* proper cleanup of previously used entries before putting them back in the pool
|
||||
* the extra new line at the end of message in text formatter has been removed
|
||||
* a new global public API to check if a level is activated: IsLevelEnabled
|
||||
* the following methods have been added to the Logger object
|
||||
* IsLevelEnabled
|
||||
* SetFormatter
|
||||
* SetOutput
|
||||
* ReplaceHooks
|
||||
* introduction of go module
|
||||
* an indent configuration for the json formatter
|
||||
* output colour support for windows
|
||||
* the field sort function is now configurable for text formatter
|
||||
* the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
|
||||
|
||||
# 1.0.6
|
||||
|
||||
This new release introduces:
|
||||
* a new api WithTime which allows to easily force the time of the log entry
|
||||
which is mostly useful for logger wrapper
|
||||
* a fix reverting the immutability of the entry given as parameter to the hooks
|
||||
a new configuration field of the json formatter in order to put all the fields
|
||||
in a nested dictionnary
|
||||
* a new SetOutput method in the Logger
|
||||
* a new configuration of the textformatter to configure the name of the default keys
|
||||
* a new configuration of the text formatter to disable the level truncation
|
||||
|
||||
# 1.0.5
|
||||
|
||||
* Fix hooks race (#707)
|
||||
* Fix panic deadlock (#695)
|
||||
|
||||
# 1.0.4
|
||||
|
||||
* Fix race when adding hooks (#612)
|
||||
* Fix terminal check in AppEngine (#635)
|
||||
|
||||
# 1.0.3
|
||||
|
||||
* Replace example files with testable examples
|
||||
|
||||
# 1.0.2
|
||||
|
||||
* bug: quote non-string values in text formatter (#583)
|
||||
* Make (*Logger) SetLevel a public method
|
||||
|
||||
# 1.0.1
|
||||
|
||||
* bug: fix escaping in text formatter (#575)
|
||||
|
||||
# 1.0.0
|
||||
|
||||
* Officially changed name to lower-case
|
||||
* bug: colors on Windows 10 (#541)
|
||||
* bug: fix race in accessing level (#512)
|
||||
|
||||
# 0.11.5
|
||||
|
||||
* feature: add writer and writerlevel to entry (#372)
|
||||
|
||||
# 0.11.4
|
||||
|
||||
* bug: fix undefined variable on solaris (#493)
|
||||
|
||||
# 0.11.3
|
||||
|
||||
* formatter: configure quoting of empty values (#484)
|
||||
* formatter: configure quoting character (default is `"`) (#484)
|
||||
* bug: fix not importing io correctly in non-linux environments (#481)
|
||||
|
||||
# 0.11.2
|
||||
|
||||
* bug: fix windows terminal detection (#476)
|
||||
|
||||
# 0.11.1
|
||||
|
||||
* bug: fix tty detection with custom out (#471)
|
||||
|
||||
# 0.11.0
|
||||
|
||||
* performance: Use bufferpool to allocate (#370)
|
||||
* terminal: terminal detection for app-engine (#343)
|
||||
* feature: exit handler (#375)
|
||||
|
||||
# 0.10.0
|
||||
|
||||
* feature: Add a test hook (#180)
|
||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||
* performance: avoid re-allocations on `WithFields` (#335)
|
||||
|
||||
# 0.9.0
|
||||
|
||||
* logrus/text_formatter: don't emit empty msg
|
||||
* logrus/hooks/airbrake: move out of main repository
|
||||
* logrus/hooks/sentry: move out of main repository
|
||||
* logrus/hooks/papertrail: move out of main repository
|
||||
* logrus/hooks/bugsnag: move out of main repository
|
||||
* logrus/core: run tests with `-race`
|
||||
* logrus/core: detect TTY based on `stderr`
|
||||
* logrus/core: support `WithError` on logger
|
||||
* logrus/core: Solaris support
|
||||
|
||||
# 0.8.7
|
||||
|
||||
* logrus/core: fix possible race (#216)
|
||||
* logrus/doc: small typo fixes and doc improvements
|
||||
|
||||
|
||||
# 0.8.6
|
||||
|
||||
* hooks/raven: allow passing an initialized client
|
||||
|
||||
# 0.8.5
|
||||
|
||||
* logrus/core: revert #208
|
||||
|
||||
# 0.8.4
|
||||
|
||||
* formatter/text: fix data race (#218)
|
||||
|
||||
# 0.8.3
|
||||
|
||||
* logrus/core: fix entry log level (#208)
|
||||
* logrus/core: improve performance of text formatter by 40%
|
||||
* logrus/core: expose `LevelHooks` type
|
||||
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||
* formatter/text: print structs more verbosely
|
||||
|
||||
# 0.8.2
|
||||
|
||||
* logrus: fix more Fatal family functions
|
||||
|
||||
# 0.8.1
|
||||
|
||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||
|
||||
# 0.8.0
|
||||
|
||||
* logrus: defaults to stderr instead of stdout
|
||||
* hooks/sentry: add special field for `*http.Request`
|
||||
* formatter/text: ignore Windows for colors
|
||||
|
||||
# 0.7.3
|
||||
|
||||
* formatter/\*: allow configuration of timestamp layout
|
||||
|
||||
# 0.7.2
|
||||
|
||||
* formatter/text: Add configuration option for time format (#158)
|
||||
495
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
495
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
@@ -1,495 +0,0 @@
|
||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/sirupsen/logrus) [](https://godoc.org/github.com/sirupsen/logrus)
|
||||
|
||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||
the standard library logger.
|
||||
|
||||
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
||||
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
||||
this caused issues in the community and we needed a standard. Some environments
|
||||
experienced problems with the upper-case variant, so the lower-case was decided.
|
||||
Everything using `logrus` will need to use the lower-case:
|
||||
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
||||
|
||||
To fix Glide, see [these
|
||||
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
||||
For an in-depth explanation of the casing issue, see [this
|
||||
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
||||
|
||||
**Are you interested in assisting in maintaining Logrus?** Currently I have a
|
||||
lot of obligations, and I am unable to provide Logrus with the maintainership it
|
||||
needs. If you'd like to help, please reach out to me at `simon at author's
|
||||
username dot com`.
|
||||
|
||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||
plain text):
|
||||
|
||||

|
||||
|
||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||
or Splunk:
|
||||
|
||||
```json
|
||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||
|
||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||
|
||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||
```
|
||||
|
||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||
attached, the output is compatible with the
|
||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||
|
||||
```text
|
||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||
```
|
||||
To ensure this behaviour even if a TTY is attached, set your formatter as follows:
|
||||
|
||||
```go
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
DisableColors: true,
|
||||
FullTimestamp: true,
|
||||
})
|
||||
```
|
||||
|
||||
#### Logging Method Name
|
||||
|
||||
If you wish to add the calling method as a field, instruct the logger via:
|
||||
```go
|
||||
log.SetReportCaller(true)
|
||||
```
|
||||
This adds the caller as 'method' like so:
|
||||
|
||||
```json
|
||||
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
|
||||
"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
|
||||
```
|
||||
|
||||
```text
|
||||
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
|
||||
```
|
||||
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
||||
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
||||
environment via benchmarks:
|
||||
```
|
||||
go test -bench=.*CallerTracing
|
||||
```
|
||||
|
||||
|
||||
#### Case-sensitivity
|
||||
|
||||
The organization's name was changed to lower-case--and this will not be changed
|
||||
back. If you are getting import conflicts due to case sensitivity, please use
|
||||
the lower-case import: `github.com/sirupsen/logrus`.
|
||||
|
||||
#### Example
|
||||
|
||||
The simplest way to use Logrus is simply the package-level exported logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
}).Info("A walrus appears")
|
||||
}
|
||||
```
|
||||
|
||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||
want:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Log as JSON instead of the default ASCII formatter.
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
|
||||
// Output to stdout instead of the default stderr
|
||||
// Can be any io.Writer, see below for File example
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
// Only log the warning severity or above.
|
||||
log.SetLevel(log.WarnLevel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
|
||||
// A common pattern is to re-use fields between logging statements by re-using
|
||||
// the logrus.Entry returned from WithFields()
|
||||
contextLogger := log.WithFields(log.Fields{
|
||||
"common": "this is a common field",
|
||||
"other": "I also should be logged always",
|
||||
})
|
||||
|
||||
contextLogger.Info("I'll be logged with common and other field")
|
||||
contextLogger.Info("Me too")
|
||||
}
|
||||
```
|
||||
|
||||
For more advanced usage such as logging to multiple locations from the same
|
||||
application, you can also create an instance of the `logrus` Logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Create a new instance of the logger. You can have any number of instances.
|
||||
var log = logrus.New()
|
||||
|
||||
func main() {
|
||||
// The API for setting attributes is a little different than the package level
|
||||
// exported logger. See Godoc.
|
||||
log.Out = os.Stdout
|
||||
|
||||
// You could set this to any `io.Writer` such as a file
|
||||
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
|
||||
// if err == nil {
|
||||
// log.Out = file
|
||||
// } else {
|
||||
// log.Info("Failed to log to file, using default stderr")
|
||||
// }
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
}
|
||||
```
|
||||
|
||||
#### Fields
|
||||
|
||||
Logrus encourages careful, structured logging through logging fields instead of
|
||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||
to send event %s to topic %s with key %d")`, you should log the much more
|
||||
discoverable:
|
||||
|
||||
```go
|
||||
log.WithFields(log.Fields{
|
||||
"event": event,
|
||||
"topic": topic,
|
||||
"key": key,
|
||||
}).Fatal("Failed to send event")
|
||||
```
|
||||
|
||||
We've found this API forces you to think about logging in a way that produces
|
||||
much more useful logging messages. We've been in countless situations where just
|
||||
a single added field to a log statement that was already there would've saved us
|
||||
hours. The `WithFields` call is optional.
|
||||
|
||||
In general, with Logrus using any of the `printf`-family functions should be
|
||||
seen as a hint you should add a field, however, you can still use the
|
||||
`printf`-family functions with Logrus.
|
||||
|
||||
#### Default Fields
|
||||
|
||||
Often it's helpful to have fields _always_ attached to log statements in an
|
||||
application or parts of one. For example, you may want to always log the
|
||||
`request_id` and `user_ip` in the context of a request. Instead of writing
|
||||
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
||||
every line, you can create a `logrus.Entry` to pass around instead:
|
||||
|
||||
```go
|
||||
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
|
||||
requestLogger.Info("something happened on that request") # will log request_id and user_ip
|
||||
requestLogger.Warn("something not great happened")
|
||||
```
|
||||
|
||||
#### Hooks
|
||||
|
||||
You can add hooks for logging levels. For example to send errors to an exception
|
||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||
multiple places simultaneously, e.g. syslog.
|
||||
|
||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||
`init`:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
|
||||
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||
|
||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
if err != nil {
|
||||
log.Error("Unable to connect to local syslog daemon")
|
||||
} else {
|
||||
log.AddHook(hook)
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||
|
||||
A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
||||
|
||||
|
||||
#### Level logging
|
||||
|
||||
Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
|
||||
|
||||
```go
|
||||
log.Trace("Something very low level.")
|
||||
log.Debug("Useful debugging information.")
|
||||
log.Info("Something noteworthy happened!")
|
||||
log.Warn("You should probably take a look at this.")
|
||||
log.Error("Something failed but I'm not quitting.")
|
||||
// Calls os.Exit(1) after logging
|
||||
log.Fatal("Bye.")
|
||||
// Calls panic() after logging
|
||||
log.Panic("I'm bailing.")
|
||||
```
|
||||
|
||||
You can set the logging level on a `Logger`, then it will only log entries with
|
||||
that severity or anything above it:
|
||||
|
||||
```go
|
||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||
log.SetLevel(log.InfoLevel)
|
||||
```
|
||||
|
||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||
environment if your application has that.
|
||||
|
||||
#### Entries
|
||||
|
||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||
automatically added to all logging events:
|
||||
|
||||
1. `time`. The timestamp when the entry was created.
|
||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||
the `AddFields` call. E.g. `Failed to send event.`
|
||||
3. `level`. The logging level. E.g. `info`.
|
||||
|
||||
#### Environments
|
||||
|
||||
Logrus has no notion of environment.
|
||||
|
||||
If you wish for hooks and formatters to only be used in specific environments,
|
||||
you should handle that yourself. For example, if your application has a global
|
||||
variable `Environment`, which is a string representation of the environment you
|
||||
could do:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
init() {
|
||||
// do something here to set environment depending on an environment variable
|
||||
// or command-line flag
|
||||
if Environment == "production" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
} else {
|
||||
// The TextFormatter is default, you don't actually have to do this.
|
||||
log.SetFormatter(&log.TextFormatter{})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration is how `logrus` was intended to be used, but JSON in
|
||||
production is mostly only useful if you do log aggregation with tools like
|
||||
Splunk or Logstash.
|
||||
|
||||
#### Formatters
|
||||
|
||||
The built-in logging formatters are:
|
||||
|
||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||
without colors.
|
||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||
field to `true`. To force no colored output even if there is a TTY set the
|
||||
`DisableColors` field to `true`. For Windows, see
|
||||
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
||||
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
||||
truncation set the `DisableLevelTruncation` field to `true`.
|
||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
||||
|
||||
Third party logging formatters:
|
||||
|
||||
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
||||
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
||||
|
||||
You can define your formatter by implementing the `Formatter` interface,
|
||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||
default ones (see Entries section above):
|
||||
|
||||
```go
|
||||
type MyJSONFormatter struct {
|
||||
}
|
||||
|
||||
log.SetFormatter(new(MyJSONFormatter))
|
||||
|
||||
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
// Note this doesn't include Time, Level and Message which are available on
|
||||
// the Entry. Consult `godoc` on information about those fields or read the
|
||||
// source of the official loggers.
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Logger as an `io.Writer`
|
||||
|
||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||
|
||||
```go
|
||||
w := logger.Writer()
|
||||
defer w.Close()
|
||||
|
||||
srv := http.Server{
|
||||
// create a stdlib log.Logger that writes to
|
||||
// logrus.Logger.
|
||||
ErrorLog: log.New(w, "", 0),
|
||||
}
|
||||
```
|
||||
|
||||
Each line written to that writer will be printed the usual way, using formatters
|
||||
and hooks. The level for those entries is `info`.
|
||||
|
||||
This means that we can override the standard library logger easily:
|
||||
|
||||
```go
|
||||
logger := logrus.New()
|
||||
logger.Formatter = &logrus.JSONFormatter{}
|
||||
|
||||
// Use logrus for standard log output
|
||||
// Note that `log` here references stdlib's log
|
||||
// Not logrus imported under the name `log`.
|
||||
log.SetOutput(logger.Writer())
|
||||
```
|
||||
|
||||
#### Rotation
|
||||
|
||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||
external program (like `logrotate(8)`) that can compress and delete old log
|
||||
entries. It should not be a feature of the application-level logger.
|
||||
|
||||
#### Tools
|
||||
|
||||
| Tool | Description |
|
||||
| ---- | ----------- |
|
||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
||||
|
||||
#### Testing
|
||||
|
||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||
|
||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||
|
||||
```go
|
||||
import(
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSomething(t*testing.T){
|
||||
logger, hook := test.NewNullLogger()
|
||||
logger.Error("Helloerror")
|
||||
|
||||
assert.Equal(t, 1, len(hook.Entries))
|
||||
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
||||
|
||||
hook.Reset()
|
||||
assert.Nil(t, hook.LastEntry())
|
||||
}
|
||||
```
|
||||
|
||||
#### Fatal handlers
|
||||
|
||||
Logrus can register one or more functions that will be called when any `fatal`
|
||||
level message is logged. The registered handlers will be executed before
|
||||
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
|
||||
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
||||
|
||||
```
|
||||
...
|
||||
handler := func() {
|
||||
// gracefully shutdown something...
|
||||
}
|
||||
logrus.RegisterExitHandler(handler)
|
||||
...
|
||||
```
|
||||
|
||||
#### Thread safety
|
||||
|
||||
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
|
||||
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
||||
|
||||
Situation when locking is not needed includes:
|
||||
|
||||
* You have no hooks registered, or hooks calling is already thread-safe.
|
||||
|
||||
* Writing to logger.Out is already thread-safe, for example:
|
||||
|
||||
1) logger.Out is protected by locks.
|
||||
|
||||
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
|
||||
|
||||
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
||||
14
vendor/github.com/Sirupsen/logrus/appveyor.yml
generated
vendored
14
vendor/github.com/Sirupsen/logrus/appveyor.yml
generated
vendored
@@ -1,14 +0,0 @@
|
||||
version: "{build}"
|
||||
platform: x64
|
||||
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
install:
|
||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
- go version
|
||||
build_script:
|
||||
- go get -t
|
||||
- go test
|
||||
11
vendor/github.com/Sirupsen/logrus/go.mod
generated
vendored
11
vendor/github.com/Sirupsen/logrus/go.mod
generated
vendored
@@ -1,11 +0,0 @@
|
||||
module github.com/sirupsen/logrus
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33
|
||||
)
|
||||
15
vendor/github.com/Sirupsen/logrus/go.sum
generated
vendored
15
vendor/github.com/Sirupsen/logrus/go.sum
generated
vendored
@@ -1,15 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
25
vendor/github.com/aead/chacha20/.gitignore
generated
vendored
25
vendor/github.com/aead/chacha20/.gitignore
generated
vendored
@@ -1,25 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.vscode
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
25
vendor/github.com/aead/chacha20/.travis.yml
generated
vendored
25
vendor/github.com/aead/chacha20/.travis.yml
generated
vendored
@@ -1,25 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
|
||||
env:
|
||||
- TRAVIS_GOARCH=amd64
|
||||
- TRAVIS_GOARCH=386
|
||||
|
||||
before_install:
|
||||
- export GOARCH=$TRAVIS_GOARCH
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
before_script:
|
||||
- go get -u github.com/klauspost/asmfmt/cmd/asmfmt
|
||||
|
||||
script:
|
||||
- diff -au <(gofmt -d .) <(printf "")
|
||||
- diff -au <(asmfmt -d .) <(printf "")
|
||||
- go test -v ./...
|
||||
82
vendor/github.com/aead/chacha20/README.md
generated
vendored
82
vendor/github.com/aead/chacha20/README.md
generated
vendored
@@ -1,82 +0,0 @@
|
||||
[](https://godoc.org/github.com/aead/chacha20)
|
||||
[](https://travis-ci.org/aead/chacha20)
|
||||
[](https://goreportcard.com/report/aead/chacha20)
|
||||
|
||||
## The ChaCha20 stream cipher
|
||||
|
||||
ChaCha is a stream cipher family created by Daniel J. Bernstein.
|
||||
The most common ChaCha variant is ChaCha20 (20 rounds). ChaCha20 is
|
||||
standardized in [RFC 7539](https://tools.ietf.org/html/rfc7539 "RFC 7539").
|
||||
|
||||
This package provides implementations of three ChaCha versions:
|
||||
- ChaCha20 with a 64 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
|
||||
- ChaCha20 with a 96 bit nonce (can en/decrypt up to 2^32 * 64 bytes ~ 256 GB for one key-nonce combination)
|
||||
- XChaCha20 with a 192 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
|
||||
|
||||
Furthermore the chacha sub package implements ChaCha20/12 and ChaCha20/8.
|
||||
These versions use 12 or 8 rounds instead of 20.
|
||||
But it's recommended to use ChaCha20 (with 20 rounds) - it will be fast enough for almost all purposes.
|
||||
|
||||
### Installation
|
||||
Install in your GOPATH: `go get -u github.com/aead/chacha20`
|
||||
|
||||
### Requirements
|
||||
All go versions >= 1.8.7 are supported.
|
||||
The code may also work on Go 1.7 but this is not tested.
|
||||
|
||||
### Performance
|
||||
|
||||
#### AMD64
|
||||
Hardware: Intel i7-6500U 2.50GHz x 2
|
||||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
|
||||
Go version: 1.8.0
|
||||
```
|
||||
AVX2
|
||||
name speed cpb
|
||||
ChaCha20_64-4 573MB/s ± 0% 4.16
|
||||
ChaCha20_1K-4 2.19GB/s ± 0% 1.06
|
||||
XChaCha20_64-4 261MB/s ± 0% 9.13
|
||||
XChaCha20_1K-4 1.69GB/s ± 4% 1.37
|
||||
XORKeyStream64-4 474MB/s ± 2% 5.02
|
||||
XORKeyStream1K-4 2.09GB/s ± 1% 1.11
|
||||
XChaCha20_XORKeyStream64-4 262MB/s ± 0% 9.09
|
||||
XChaCha20_XORKeyStream1K-4 1.71GB/s ± 1% 1.36
|
||||
|
||||
SSSE3
|
||||
name speed cpb
|
||||
ChaCha20_64-4 583MB/s ± 0% 4.08
|
||||
ChaCha20_1K-4 1.15GB/s ± 1% 2.02
|
||||
XChaCha20_64-4 267MB/s ± 0% 8.92
|
||||
XChaCha20_1K-4 984MB/s ± 5% 2.42
|
||||
XORKeyStream64-4 492MB/s ± 1% 4.84
|
||||
XORKeyStream1K-4 1.10GB/s ± 5% 2.11
|
||||
XChaCha20_XORKeyStream64-4 266MB/s ± 0% 8.96
|
||||
XChaCha20_XORKeyStream1K-4 1.00GB/s ± 2% 2.32
|
||||
```
|
||||
#### 386
|
||||
Hardware: Intel i7-6500U 2.50GHz x 2
|
||||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
|
||||
Go version: 1.8.0
|
||||
```
|
||||
SSSE3
|
||||
name speed cpb
|
||||
ChaCha20_64-4 570MB/s ± 0% 4.18
|
||||
ChaCha20_1K-4 650MB/s ± 0% 3.66
|
||||
XChaCha20_64-4 223MB/s ± 0% 10.69
|
||||
XChaCha20_1K-4 584MB/s ± 1% 4.08
|
||||
XORKeyStream64-4 392MB/s ± 1% 6.08
|
||||
XORKeyStream1K-4 629MB/s ± 1% 3.79
|
||||
XChaCha20_XORKeyStream64-4 222MB/s ± 0% 10.73
|
||||
XChaCha20_XORKeyStream1K-4 585MB/s ± 0% 4.07
|
||||
|
||||
SSE2
|
||||
name speed cpb
|
||||
ChaCha20_64-4 509MB/s ± 0% 4.68
|
||||
ChaCha20_1K-4 553MB/s ± 2% 4.31
|
||||
XChaCha20_64-4 201MB/s ± 0% 11.86
|
||||
XChaCha20_1K-4 498MB/s ± 4% 4.78
|
||||
XORKeyStream64-4 359MB/s ± 1% 6.64
|
||||
XORKeyStream1K-4 545MB/s ± 0% 4.37
|
||||
XChaCha20_XORKeyStream64-4 201MB/s ± 1% 11.86
|
||||
XChaCha20_XORKeyStream1K-4 507MB/s ± 0% 4.70
|
||||
```
|
||||
@@ -8,20 +8,22 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errBadJSONDoc = fmt.Errorf("Invalid JSON Document")
|
||||
var errBadJSONDoc = fmt.Errorf("invalid JSON Document")
|
||||
|
||||
type JsonPatchOperation struct {
|
||||
type JsonPatchOperation = Operation
|
||||
|
||||
type Operation struct {
|
||||
Operation string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (j *JsonPatchOperation) Json() string {
|
||||
func (j *Operation) Json() string {
|
||||
b, _ := json.Marshal(j)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (j *JsonPatchOperation) MarshalJSON() ([]byte, error) {
|
||||
func (j *Operation) MarshalJSON() ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteString("{")
|
||||
b.WriteString(fmt.Sprintf(`"op":"%s"`, j.Operation))
|
||||
@@ -39,14 +41,14 @@ func (j *JsonPatchOperation) MarshalJSON() ([]byte, error) {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
type ByPath []JsonPatchOperation
|
||||
type ByPath []Operation
|
||||
|
||||
func (a ByPath) Len() int { return len(a) }
|
||||
func (a ByPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }
|
||||
|
||||
func NewPatch(operation, path string, value interface{}) JsonPatchOperation {
|
||||
return JsonPatchOperation{Operation: operation, Path: path, Value: value}
|
||||
func NewPatch(operation, path string, value interface{}) Operation {
|
||||
return Operation{Operation: operation, Path: path, Value: value}
|
||||
}
|
||||
|
||||
// CreatePatch creates a patch as specified in http://jsonpatch.com/
|
||||
@@ -55,7 +57,7 @@ func NewPatch(operation, path string, value interface{}) JsonPatchOperation {
|
||||
// The function will return an array of JsonPatchOperations
|
||||
//
|
||||
// An error will be returned if any of the two documents are invalid.
|
||||
func CreatePatch(a, b []byte) ([]JsonPatchOperation, error) {
|
||||
func CreatePatch(a, b []byte) ([]Operation, error) {
|
||||
aI := map[string]interface{}{}
|
||||
bI := map[string]interface{}{}
|
||||
err := json.Unmarshal(a, &aI)
|
||||
@@ -66,7 +68,7 @@ func CreatePatch(a, b []byte) ([]JsonPatchOperation, error) {
|
||||
if err != nil {
|
||||
return nil, errBadJSONDoc
|
||||
}
|
||||
return diff(aI, bI, "", []JsonPatchOperation{})
|
||||
return diff(aI, bI, "", []Operation{})
|
||||
}
|
||||
|
||||
// Returns true if the values matches (must be json types)
|
||||
@@ -78,22 +80,25 @@ func matchesValue(av, bv interface{}) bool {
|
||||
}
|
||||
switch at := av.(type) {
|
||||
case string:
|
||||
bt := bv.(string)
|
||||
if bt == at {
|
||||
bt, ok := bv.(string)
|
||||
if ok && bt == at {
|
||||
return true
|
||||
}
|
||||
case float64:
|
||||
bt := bv.(float64)
|
||||
if bt == at {
|
||||
bt, ok := bv.(float64)
|
||||
if ok && bt == at {
|
||||
return true
|
||||
}
|
||||
case bool:
|
||||
bt := bv.(bool)
|
||||
if bt == at {
|
||||
bt, ok := bv.(bool)
|
||||
if ok && bt == at {
|
||||
return true
|
||||
}
|
||||
case map[string]interface{}:
|
||||
bt := bv.(map[string]interface{})
|
||||
bt, ok := bv.(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for key := range at {
|
||||
if !matchesValue(at[key], bt[key]) {
|
||||
return false
|
||||
@@ -106,7 +111,10 @@ func matchesValue(av, bv interface{}) bool {
|
||||
}
|
||||
return true
|
||||
case []interface{}:
|
||||
bt := bv.([]interface{})
|
||||
bt, ok := bv.([]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if len(bt) != len(at) {
|
||||
return false
|
||||
}
|
||||
@@ -148,7 +156,7 @@ func makePath(path string, newPart interface{}) string {
|
||||
}
|
||||
|
||||
// diff returns the (recursive) difference between a and b as an array of JsonPatchOperations.
|
||||
func diff(a, b map[string]interface{}, path string, patch []JsonPatchOperation) ([]JsonPatchOperation, error) {
|
||||
func diff(a, b map[string]interface{}, path string, patch []Operation) ([]Operation, error) {
|
||||
for key, bv := range b {
|
||||
p := makePath(path, key)
|
||||
av, ok := a[key]
|
||||
@@ -157,11 +165,6 @@ func diff(a, b map[string]interface{}, path string, patch []JsonPatchOperation)
|
||||
patch = append(patch, NewPatch("add", p, bv))
|
||||
continue
|
||||
}
|
||||
// If types have changed, replace completely
|
||||
if reflect.TypeOf(av) != reflect.TypeOf(bv) {
|
||||
patch = append(patch, NewPatch("replace", p, bv))
|
||||
continue
|
||||
}
|
||||
// Types are the same, compare values
|
||||
var err error
|
||||
patch, err = handleValues(av, bv, p, patch)
|
||||
@@ -181,7 +184,21 @@ func diff(a, b map[string]interface{}, path string, patch []JsonPatchOperation)
|
||||
return patch, nil
|
||||
}
|
||||
|
||||
func handleValues(av, bv interface{}, p string, patch []JsonPatchOperation) ([]JsonPatchOperation, error) {
|
||||
func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation, error) {
|
||||
{
|
||||
at := reflect.TypeOf(av)
|
||||
bt := reflect.TypeOf(bv)
|
||||
if at == nil && bt == nil {
|
||||
// do nothing
|
||||
return patch, nil
|
||||
} else if at == nil && bt != nil {
|
||||
return append(patch, NewPatch("add", p, bv)), nil
|
||||
} else if at != bt {
|
||||
// If types have changed, replace completely (preserves null in destination)
|
||||
return append(patch, NewPatch("replace", p, bv)), nil
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
switch at := av.(type) {
|
||||
case map[string]interface{}:
|
||||
@@ -195,63 +212,125 @@ func handleValues(av, bv interface{}, p string, patch []JsonPatchOperation) ([]J
|
||||
patch = append(patch, NewPatch("replace", p, bv))
|
||||
}
|
||||
case []interface{}:
|
||||
bt, ok := bv.([]interface{})
|
||||
if !ok {
|
||||
// array replaced by non-array
|
||||
patch = append(patch, NewPatch("replace", p, bv))
|
||||
} else if len(at) != len(bt) {
|
||||
// arrays are not the same length
|
||||
patch = append(patch, compareArray(at, bt, p)...)
|
||||
|
||||
bt := bv.([]interface{})
|
||||
if isSimpleArray(at) && isSimpleArray(bt) {
|
||||
patch = append(patch, compareEditDistance(at, bt, p)...)
|
||||
} else {
|
||||
for i := range bt {
|
||||
n := min(len(at), len(bt))
|
||||
for i := len(at) - 1; i >= n; i-- {
|
||||
patch = append(patch, NewPatch("remove", makePath(p, i), nil))
|
||||
}
|
||||
for i := n; i < len(bt); i++ {
|
||||
patch = append(patch, NewPatch("add", makePath(p, i), bt[i]))
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
var err error
|
||||
patch, err = handleValues(at[i], bt[i], makePath(p, i), patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
case nil:
|
||||
switch bv.(type) {
|
||||
case nil:
|
||||
// Both nil, fine.
|
||||
default:
|
||||
patch = append(patch, NewPatch("add", p, bv))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown type:%T ", av))
|
||||
}
|
||||
return patch, nil
|
||||
}
|
||||
|
||||
func compareArray(av, bv []interface{}, p string) []JsonPatchOperation {
|
||||
retval := []JsonPatchOperation{}
|
||||
// var err error
|
||||
for i, v := range av {
|
||||
found := false
|
||||
for _, v2 := range bv {
|
||||
if reflect.DeepEqual(v, v2) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
retval = append(retval, NewPatch("remove", makePath(p, i), nil))
|
||||
}
|
||||
func isBasicType(a interface{}) bool {
|
||||
switch a.(type) {
|
||||
case string, float64, bool:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range bv {
|
||||
found := false
|
||||
for _, v2 := range av {
|
||||
if reflect.DeepEqual(v, v2) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
retval = append(retval, NewPatch("add", makePath(p, i), v))
|
||||
}
|
||||
}
|
||||
|
||||
return retval
|
||||
return true
|
||||
}
|
||||
|
||||
func isSimpleArray(a []interface{}) bool {
|
||||
for i := range a {
|
||||
switch a[i].(type) {
|
||||
case string, float64, bool:
|
||||
default:
|
||||
val := reflect.ValueOf(a[i])
|
||||
if val.Kind() == reflect.Map {
|
||||
for _, k := range val.MapKeys() {
|
||||
av := val.MapIndex(k)
|
||||
if av.Kind() == reflect.Ptr || av.Kind() == reflect.Interface {
|
||||
if av.IsNil() {
|
||||
continue
|
||||
}
|
||||
av = av.Elem()
|
||||
}
|
||||
if av.Kind() != reflect.String && av.Kind() != reflect.Float64 && av.Kind() != reflect.Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm
|
||||
// Adapted from https://github.com/texttheater/golang-levenshtein
|
||||
func compareEditDistance(s, t []interface{}, p string) []Operation {
|
||||
m := len(s)
|
||||
n := len(t)
|
||||
|
||||
d := make([][]int, m+1)
|
||||
for i := 0; i <= m; i++ {
|
||||
d[i] = make([]int, n+1)
|
||||
d[i][0] = i
|
||||
}
|
||||
for j := 0; j <= n; j++ {
|
||||
d[0][j] = j
|
||||
}
|
||||
|
||||
for j := 1; j <= n; j++ {
|
||||
for i := 1; i <= m; i++ {
|
||||
if reflect.DeepEqual(s[i-1], t[j-1]) {
|
||||
d[i][j] = d[i-1][j-1] // no op required
|
||||
} else {
|
||||
del := d[i-1][j] + 1
|
||||
add := d[i][j-1] + 1
|
||||
rep := d[i-1][j-1] + 1
|
||||
d[i][j] = min(rep, min(add, del))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return backtrace(s, t, p, m, n, d)
|
||||
}
|
||||
|
||||
func min(x int, y int) int {
|
||||
if y < x {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func backtrace(s, t []interface{}, p string, i int, j int, matrix [][]int) []Operation {
|
||||
if i > 0 && matrix[i-1][j]+1 == matrix[i][j] {
|
||||
op := NewPatch("remove", makePath(p, i-1), nil)
|
||||
return append([]Operation{op}, backtrace(s, t, p, i-1, j, matrix)...)
|
||||
}
|
||||
if j > 0 && matrix[i][j-1]+1 == matrix[i][j] {
|
||||
op := NewPatch("add", makePath(p, i), t[j-1])
|
||||
return append([]Operation{op}, backtrace(s, t, p, i, j-1, matrix)...)
|
||||
}
|
||||
if i > 0 && j > 0 && matrix[i-1][j-1]+1 == matrix[i][j] {
|
||||
if isBasicType(s[0]) {
|
||||
op := NewPatch("replace", makePath(p, i-1), t[j-1])
|
||||
return append([]Operation{op}, backtrace(s, t, p, i-1, j-1, matrix)...)
|
||||
}
|
||||
|
||||
p2, _ := handleValues(s[j-1], t[j-1], makePath(p, i-1), []Operation{})
|
||||
return append(p2, backtrace(s, t, p, i-1, j-1, matrix)...)
|
||||
}
|
||||
if i > 0 && j > 0 && matrix[i-1][j-1] == matrix[i][j] {
|
||||
return backtrace(s, t, p, i-1, j-1, matrix)
|
||||
}
|
||||
return []Operation{}
|
||||
}
|
||||
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
2388
vendor/github.com/beorn7/perks/quantile/exampledata.txt
generated
vendored
File diff suppressed because it is too large
Load Diff
94
vendor/github.com/bifurcation/mint/README.md
generated
vendored
94
vendor/github.com/bifurcation/mint/README.md
generated
vendored
@@ -1,94 +0,0 @@
|
||||

|
||||
|
||||
mint - A Minimal TLS 1.3 stack
|
||||
==============================
|
||||
|
||||
[](https://circleci.com/gh/bifurcation/mint)
|
||||
|
||||
This project is primarily a learning effort for me to understand the [TLS
|
||||
1.3](http://tlswg.github.io/tls13-spec/) protocol. The goal is to arrive at a
|
||||
pretty complete implementation of TLS 1.3, with minimal, elegant code that
|
||||
demonstrates how things work. Testing is a priority to ensure correctness, but
|
||||
otherwise, the quality of the software engineering might not be at a level where
|
||||
it makes sense to integrate this with other libraries. Backward compatibility
|
||||
is not an objective.
|
||||
|
||||
We borrow liberally from the [Go TLS
|
||||
library](https://golang.org/pkg/crypto/tls/), especially where TLS 1.3 aligns
|
||||
with earlier TLS versions. However, unnecessary parts will be ruthlessly cut
|
||||
off.
|
||||
|
||||
## DTLS Support
|
||||
|
||||
Mint has partial support for DTLS, but that support is not yet complete
|
||||
and may still contain serious defects.
|
||||
|
||||
|
||||
## Quickstart
|
||||
|
||||
Installation is the same as for any other Go package:
|
||||
|
||||
```
|
||||
go get github.com/bifurcation/mint
|
||||
```
|
||||
|
||||
The API is pretty much the same as for the TLS module, with `Dial` and `Listen`
|
||||
methods wrapping the underlying socket APIs.
|
||||
|
||||
```
|
||||
conn, err := mint.Dial("tcp", "localhost:4430", &mint.Config{...})
|
||||
...
|
||||
listener, err := mint.Listen("tcp", "localhost:4430", &mint.Config{...})
|
||||
```
|
||||
|
||||
Documentation is available on
|
||||
[godoc.org](https://godoc.org/github.com/bifurcation/mint)
|
||||
|
||||
|
||||
## Interoperability testing
|
||||
|
||||
The `mint-client` and `mint-server` executables are included to make it easy to
|
||||
do basic interoperability tests with other TLS 1.3 implementations. The steps
|
||||
for testing against NSS are as follows.
|
||||
|
||||
```
|
||||
# Install mint
|
||||
go get github.com/bifurcation/mint
|
||||
|
||||
# Environment for NSS (you'll probably want a new directory)
|
||||
NSS_ROOT=<whereever you want to put NSS>
|
||||
mkdir $NSS_ROOT
|
||||
cd $NSS_ROOT
|
||||
export USE_64=1
|
||||
export ENABLE_TLS_1_3=1
|
||||
export HOST=localhost
|
||||
export DOMSUF=localhost
|
||||
|
||||
# Build NSS
|
||||
hg clone https://hg.mozilla.org/projects/nss
|
||||
hg clone https://hg.mozilla.org/projects/nspr
|
||||
cd nss
|
||||
make nss_build_all
|
||||
|
||||
export PLATFORM=`cat $NSS_ROOT/dist/latest`
|
||||
export DYLD_LIBRARY_PATH=$NSS_ROOT/dist/$PLATFORM/lib
|
||||
export LD_LIBRARY_PATH=$NSS_ROOT/dist/$PLATFORM/lib
|
||||
|
||||
# Run NSS tests (this creates data for the server to use)
|
||||
cd tests/ssl_gtests
|
||||
./ssl_gtests.sh
|
||||
|
||||
# Test with client=mint server=NSS
|
||||
cd $NSS_ROOT
|
||||
./dist/$PLATFORM/bin/selfserv -d tests_results/security/$HOST.1/ssl_gtests/ -n rsa -p 4430
|
||||
# if you get `NSS_Init failed.`, check the path above, particularly around $HOST
|
||||
# ...
|
||||
go run $GOPATH/src/github.com/bifurcation/mint/bin/mint-client/main.go
|
||||
|
||||
# Test with client=NSS server=mint
|
||||
go run $GOPATH/src/github.com/bifurcation/mint/bin/mint-server/main.go
|
||||
# ...
|
||||
cd $NSS_ROOT
|
||||
dist/$PLATFORM/bin/tstclnt -d tests_results/security/$HOST/ssl_gtests/ -V tls1.3:tls1.3 -h 127.0.0.1 -p 4430 -o
|
||||
```
|
||||
|
||||
22
vendor/github.com/bifurcation/mint/client-state-machine.go
generated
vendored
22
vendor/github.com/bifurcation/mint/client-state-machine.go
generated
vendored
@@ -89,7 +89,7 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
|
||||
logf(logTypeHandshake, "opts: %+v", state.Opts)
|
||||
|
||||
// supported_versions, supported_groups, signature_algorithms, server_name
|
||||
sv := SupportedVersionsExtension{HandshakeType: HandshakeTypeClientHello, Versions: []uint16{supportedVersion}}
|
||||
sv := SupportedVersionsExtension{HandshakeType: HandshakeTypeClientHello, Versions: []uint16{tls13Version}}
|
||||
sni := ServerNameExtension(state.Opts.ServerName)
|
||||
sg := SupportedGroupsExtension{Groups: state.Config.Groups}
|
||||
sa := SignatureAlgorithmsExtension{Algorithms: state.Config.SignatureSchemes}
|
||||
@@ -136,6 +136,15 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
|
||||
}
|
||||
}
|
||||
|
||||
if len(state.Config.PSKModes) != 0 {
|
||||
kem := &PSKKeyExchangeModesExtension{KEModes: state.Config.PSKModes}
|
||||
err = ch.Extensions.Add(kem)
|
||||
if err != nil {
|
||||
logf(logTypeHandshake, "Error adding PSKKeyExchangeModes extension: %v", err)
|
||||
return nil, nil, AlertInternalError
|
||||
}
|
||||
}
|
||||
|
||||
// Run the external extension handler.
|
||||
if state.Config.ExtensionHandler != nil {
|
||||
err := state.Config.ExtensionHandler.Send(HandshakeTypeClientHello, &ch.Extensions)
|
||||
@@ -152,7 +161,7 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
|
||||
var offeredPSK PreSharedKey
|
||||
var earlyHash crypto.Hash
|
||||
var earlySecret []byte
|
||||
var clientEarlyTrafficKeys keySet
|
||||
var clientEarlyTrafficKeys KeySet
|
||||
var clientHello *HandshakeMessage
|
||||
if key, ok := state.Config.PSKs.Get(state.Opts.ServerName); ok {
|
||||
offeredPSK = key
|
||||
@@ -190,12 +199,6 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
|
||||
logf(logTypeHandshake, "PSK selected, but no PSKModes")
|
||||
return nil, nil, AlertInternalError
|
||||
}
|
||||
kem := &PSKKeyExchangeModesExtension{KEModes: state.Config.PSKModes}
|
||||
err = ch.Extensions.Add(kem)
|
||||
if err != nil {
|
||||
logf(logTypeHandshake, "Error adding PSKKeyExchangeModes extension: %v", err)
|
||||
return nil, nil, AlertInternalError
|
||||
}
|
||||
|
||||
// Add the shim PSK extension to the ClientHello
|
||||
logf(logTypeHandshake, "Adding PSK extension with id = %x", key.Identity)
|
||||
@@ -354,7 +357,7 @@ func (state clientStateWaitSH) Next(hr handshakeMessageReader) (HandshakeState,
|
||||
logf(logTypeHandshake, "[ClientStateWaitSH] no supported_versions extension")
|
||||
return nil, nil, AlertMissingExtension
|
||||
}
|
||||
if supportedVersions.Versions[0] != supportedVersion {
|
||||
if supportedVersions.Versions[0] != tls13Version {
|
||||
logf(logTypeHandshake, "[ClientStateWaitSH] unsupported version [%x]", supportedVersions.Versions[0])
|
||||
return nil, nil, AlertProtocolVersion
|
||||
}
|
||||
@@ -416,6 +419,7 @@ func (state clientStateWaitSH) Next(hr handshakeMessageReader) (HandshakeState,
|
||||
// mode. In DTLS, we also need to bump the sequence number.
|
||||
// This is a pre-existing defect in Mint. Issue #175.
|
||||
logf(logTypeHandshake, "[ClientStateWaitSH] -> [ClientStateStart]")
|
||||
state.hsCtx.SetVersion(tls12Version) // Everything after this should be 1.2.
|
||||
return clientStateStart{
|
||||
Config: state.Config,
|
||||
Opts: state.Opts,
|
||||
|
||||
4
vendor/github.com/bifurcation/mint/common.go
generated
vendored
4
vendor/github.com/bifurcation/mint/common.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
supportedVersion uint16 = 0x7f16 // draft-22
|
||||
tls13Version uint16 = 0x0304
|
||||
tls12Version uint16 = 0x0303
|
||||
tls10Version uint16 = 0x0301
|
||||
dtls12WireVersion uint16 = 0xfefd
|
||||
@@ -118,7 +118,7 @@ const (
|
||||
ExtensionTypeSupportedGroups ExtensionType = 10
|
||||
ExtensionTypeSignatureAlgorithms ExtensionType = 13
|
||||
ExtensionTypeALPN ExtensionType = 16
|
||||
ExtensionTypeKeyShare ExtensionType = 40
|
||||
ExtensionTypeKeyShare ExtensionType = 51
|
||||
ExtensionTypePreSharedKey ExtensionType = 41
|
||||
ExtensionTypeEarlyData ExtensionType = 42
|
||||
ExtensionTypeSupportedVersions ExtensionType = 43
|
||||
|
||||
27
vendor/github.com/bifurcation/mint/conn.go
generated
vendored
27
vendor/github.com/bifurcation/mint/conn.go
generated
vendored
@@ -129,6 +129,8 @@ type Config struct {
|
||||
NonBlocking bool
|
||||
UseDTLS bool
|
||||
|
||||
RecordLayer RecordLayerFactory
|
||||
|
||||
// The same config object can be shared among different connections, so it
|
||||
// needs its own mutex
|
||||
mutex sync.RWMutex
|
||||
@@ -270,28 +272,33 @@ type Conn struct {
|
||||
handshakeComplete bool
|
||||
|
||||
readBuffer []byte
|
||||
in, out *RecordLayer
|
||||
in, out RecordLayer
|
||||
hsCtx *HandshakeContext
|
||||
}
|
||||
|
||||
func NewConn(conn net.Conn, config *Config, isClient bool) *Conn {
|
||||
c := &Conn{conn: conn, config: config, isClient: isClient, hsCtx: &HandshakeContext{}}
|
||||
if !config.UseDTLS {
|
||||
c.in = NewRecordLayerTLS(c.conn, directionRead)
|
||||
c.out = NewRecordLayerTLS(c.conn, directionWrite)
|
||||
if config.RecordLayer == nil {
|
||||
c.in = NewRecordLayerTLS(c.conn, DirectionRead)
|
||||
c.out = NewRecordLayerTLS(c.conn, DirectionWrite)
|
||||
} else {
|
||||
c.in = config.RecordLayer.NewLayer(c.conn, DirectionRead)
|
||||
c.out = config.RecordLayer.NewLayer(c.conn, DirectionWrite)
|
||||
}
|
||||
c.hsCtx.hIn = NewHandshakeLayerTLS(c.hsCtx, c.in)
|
||||
c.hsCtx.hOut = NewHandshakeLayerTLS(c.hsCtx, c.out)
|
||||
} else {
|
||||
c.in = NewRecordLayerDTLS(c.conn, directionRead)
|
||||
c.out = NewRecordLayerDTLS(c.conn, directionWrite)
|
||||
c.in = NewRecordLayerDTLS(c.conn, DirectionRead)
|
||||
c.out = NewRecordLayerDTLS(c.conn, DirectionWrite)
|
||||
c.hsCtx.hIn = NewHandshakeLayerDTLS(c.hsCtx, c.in)
|
||||
c.hsCtx.hOut = NewHandshakeLayerDTLS(c.hsCtx, c.out)
|
||||
c.hsCtx.timeoutMS = initialTimeout
|
||||
c.hsCtx.timers = newTimerSet()
|
||||
c.hsCtx.waitingNextFlight = true
|
||||
}
|
||||
c.in.label = c.label()
|
||||
c.out.label = c.label()
|
||||
c.in.SetLabel(c.label())
|
||||
c.out.SetLabel(c.label())
|
||||
c.hsCtx.hIn.nonblocking = c.config.NonBlocking
|
||||
return c
|
||||
}
|
||||
@@ -598,7 +605,7 @@ func (c *Conn) takeAction(actionGeneric HandshakeAction) Alert {
|
||||
logf(logTypeHandshake, "%s Rekey with data still in handshake buffers", label)
|
||||
return AlertDecodeError
|
||||
}
|
||||
err := c.in.Rekey(action.epoch, action.KeySet.cipher, action.KeySet.key, action.KeySet.iv)
|
||||
err := c.in.Rekey(action.epoch, action.KeySet.Cipher, &action.KeySet)
|
||||
if err != nil {
|
||||
logf(logTypeHandshake, "%s Unable to rekey inbound: %v", label, err)
|
||||
return AlertInternalError
|
||||
@@ -606,7 +613,7 @@ func (c *Conn) takeAction(actionGeneric HandshakeAction) Alert {
|
||||
|
||||
case RekeyOut:
|
||||
logf(logTypeHandshake, "%s Rekeying out to %s: %+v", label, action.epoch.label(), action.KeySet)
|
||||
err := c.out.Rekey(action.epoch, action.KeySet.cipher, action.KeySet.key, action.KeySet.iv)
|
||||
err := c.out.Rekey(action.epoch, action.KeySet.Cipher, &action.KeySet)
|
||||
if err != nil {
|
||||
logf(logTypeHandshake, "%s Unable to rekey outbound: %v", label, err)
|
||||
return AlertInternalError
|
||||
@@ -906,7 +913,7 @@ func (c *Conn) Writable() bool {
|
||||
}
|
||||
|
||||
// If we're a client in 0-RTT, then we're writable.
|
||||
if c.isClient && c.out.cipher.epoch == EpochEarlyData {
|
||||
if c.isClient && c.out.Epoch() == EpochEarlyData {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
28
vendor/github.com/bifurcation/mint/crypto.go
generated
vendored
28
vendor/github.com/bifurcation/mint/crypto.go
generated
vendored
@@ -27,11 +27,11 @@ import (
|
||||
|
||||
var prng = rand.Reader
|
||||
|
||||
type aeadFactory func(key []byte) (cipher.AEAD, error)
|
||||
type AeadFactory func(key []byte) (cipher.AEAD, error)
|
||||
|
||||
type CipherSuiteParams struct {
|
||||
Suite CipherSuite
|
||||
Cipher aeadFactory // Cipher factory
|
||||
Cipher AeadFactory // Cipher factory
|
||||
Hash crypto.Hash // Hash function
|
||||
KeyLen int // Key length in octets
|
||||
IvLen int // IV length in octets
|
||||
@@ -546,8 +546,10 @@ const (
|
||||
// opaque label<9..255>;
|
||||
// opaque hash_value<0..255>;
|
||||
// };
|
||||
var HkdfLabelPrefix = "tls13 "
|
||||
|
||||
func hkdfEncodeLabel(labelIn string, hashValue []byte, outLen int) []byte {
|
||||
label := "tls13 " + labelIn
|
||||
label := HkdfLabelPrefix + labelIn
|
||||
|
||||
labelLen := len(label)
|
||||
hashLen := len(hashValue)
|
||||
@@ -604,18 +606,20 @@ func computeFinishedData(params CipherSuiteParams, baseKey []byte, input []byte)
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
type keySet struct {
|
||||
cipher aeadFactory
|
||||
key []byte
|
||||
iv []byte
|
||||
type KeySet struct {
|
||||
Cipher AeadFactory
|
||||
Key []byte
|
||||
Iv []byte
|
||||
Pn []byte
|
||||
}
|
||||
|
||||
func makeTrafficKeys(params CipherSuiteParams, secret []byte) keySet {
|
||||
func makeTrafficKeys(params CipherSuiteParams, secret []byte) KeySet {
|
||||
logf(logTypeCrypto, "making traffic keys: secret=%x", secret)
|
||||
return keySet{
|
||||
cipher: params.Cipher,
|
||||
key: HkdfExpandLabel(params.Hash, secret, "key", []byte{}, params.KeyLen),
|
||||
iv: HkdfExpandLabel(params.Hash, secret, "iv", []byte{}, params.IvLen),
|
||||
return KeySet{
|
||||
Cipher: params.Cipher,
|
||||
Key: HkdfExpandLabel(params.Hash, secret, "key", []byte{}, params.KeyLen),
|
||||
Iv: HkdfExpandLabel(params.Hash, secret, "iv", []byte{}, params.IvLen),
|
||||
Pn: HkdfExpandLabel(params.Hash, secret, "pn", []byte{}, params.KeyLen),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
vendor/github.com/bifurcation/mint/handshake-layer.go
generated
vendored
51
vendor/github.com/bifurcation/mint/handshake-layer.go
generated
vendored
@@ -120,7 +120,7 @@ func (h *HandshakeLayer) HandshakeMessageFromBody(body HandshakeMessageBody) (*H
|
||||
type HandshakeLayer struct {
|
||||
ctx *HandshakeContext // The handshake we are attached to
|
||||
nonblocking bool // Should we operate in nonblocking mode
|
||||
conn *RecordLayer // Used for reading/writing records
|
||||
conn RecordLayer // Used for reading/writing records
|
||||
frame *frameReader // The buffered frame reader
|
||||
datagram bool // Is this DTLS?
|
||||
msgSeq uint32 // The DTLS message sequence number
|
||||
@@ -153,7 +153,7 @@ func (d handshakeLayerFrameDetails) frameLen(hdr []byte) (int, error) {
|
||||
return int(val), nil
|
||||
}
|
||||
|
||||
func NewHandshakeLayerTLS(c *HandshakeContext, r *RecordLayer) *HandshakeLayer {
|
||||
func NewHandshakeLayerTLS(c *HandshakeContext, r RecordLayer) *HandshakeLayer {
|
||||
h := HandshakeLayer{}
|
||||
h.ctx = c
|
||||
h.conn = r
|
||||
@@ -163,7 +163,7 @@ func NewHandshakeLayerTLS(c *HandshakeContext, r *RecordLayer) *HandshakeLayer {
|
||||
return &h
|
||||
}
|
||||
|
||||
func NewHandshakeLayerDTLS(c *HandshakeContext, r *RecordLayer) *HandshakeLayer {
|
||||
func NewHandshakeLayerDTLS(c *HandshakeContext, r RecordLayer) *HandshakeLayer {
|
||||
h := HandshakeLayer{}
|
||||
h.ctx = c
|
||||
h.conn = r
|
||||
@@ -174,8 +174,15 @@ func NewHandshakeLayerDTLS(c *HandshakeContext, r *RecordLayer) *HandshakeLayer
|
||||
}
|
||||
|
||||
func (h *HandshakeLayer) readRecord() error {
|
||||
logf(logTypeVerbose, "Trying to read record")
|
||||
pt, err := h.conn.readRecordAnyEpoch()
|
||||
var pt *TLSPlaintext
|
||||
var err error
|
||||
|
||||
if h.datagram {
|
||||
logf(logTypeVerbose, "Trying to read record")
|
||||
pt, err = h.conn.(*RecordLayerImpl).ReadRecordAnyEpoch()
|
||||
} else {
|
||||
pt, err = h.conn.ReadRecord()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -204,7 +211,7 @@ func (h *HandshakeLayer) readRecord() error {
|
||||
}
|
||||
|
||||
assert(h.ctx.hIn.conn != nil)
|
||||
if pt.epoch != h.ctx.hIn.conn.cipher.epoch {
|
||||
if pt.epoch != h.ctx.hIn.conn.Epoch() {
|
||||
// This is out of order but we're dropping it.
|
||||
// TODO(ekr@rtfm.com): If server, need to retransmit Finished.
|
||||
if pt.epoch == EpochClear || pt.epoch == EpochHandshakeData {
|
||||
@@ -394,9 +401,13 @@ func (h *HandshakeLayer) ReadMessage() (*HandshakeMessage, error) {
|
||||
}
|
||||
|
||||
func (h *HandshakeLayer) QueueMessage(hm *HandshakeMessage) error {
|
||||
hm.cipher = h.conn.cipher
|
||||
h.queued = append(h.queued, hm)
|
||||
return nil
|
||||
if h.datagram {
|
||||
hm.cipher = h.conn.(*RecordLayerImpl).cipher
|
||||
h.queued = append(h.queued, hm)
|
||||
return nil
|
||||
}
|
||||
_, err := h.WriteMessages([]*HandshakeMessage{hm})
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *HandshakeLayer) SendQueuedMessages() (int, error) {
|
||||
@@ -456,22 +467,30 @@ func (h *HandshakeLayer) writeFragment(hm *HandshakeMessage, start int, room int
|
||||
buf = body
|
||||
}
|
||||
|
||||
var err error
|
||||
if h.datagram {
|
||||
// Remember that we sent this.
|
||||
h.ctx.sentFragments = append(h.ctx.sentFragments, &SentHandshakeFragment{
|
||||
hm.seq,
|
||||
start,
|
||||
len(body),
|
||||
h.conn.cipher.combineSeq(true),
|
||||
h.conn.(*RecordLayerImpl).cipher.combineSeq(true),
|
||||
false,
|
||||
})
|
||||
err = h.conn.(*RecordLayerImpl).writeRecordWithPadding(
|
||||
&TLSPlaintext{
|
||||
contentType: RecordTypeHandshake,
|
||||
fragment: buf,
|
||||
},
|
||||
hm.cipher, 0)
|
||||
} else {
|
||||
err = h.conn.WriteRecord(
|
||||
&TLSPlaintext{
|
||||
contentType: RecordTypeHandshake,
|
||||
fragment: buf,
|
||||
})
|
||||
}
|
||||
return true, start + bodylen, h.conn.writeRecordWithPadding(
|
||||
&TLSPlaintext{
|
||||
contentType: RecordTypeHandshake,
|
||||
fragment: buf,
|
||||
},
|
||||
hm.cipher, 0)
|
||||
return true, start + bodylen, err
|
||||
}
|
||||
|
||||
func (h *HandshakeLayer) WriteMessage(hm *HandshakeMessage) (int, error) {
|
||||
|
||||
2
vendor/github.com/bifurcation/mint/handshake-messages.go
generated
vendored
2
vendor/github.com/bifurcation/mint/handshake-messages.go
generated
vendored
@@ -406,7 +406,7 @@ func (cr *CertificateRequestBody) Unmarshal(data []byte) (int, error) {
|
||||
type NewSessionTicketBody struct {
|
||||
TicketLifetime uint32
|
||||
TicketAgeAdd uint32
|
||||
TicketNonce []byte `tls:"head=1,min=1"`
|
||||
TicketNonce []byte `tls:"head=1"`
|
||||
Ticket []byte `tls:"head=2,min=1"`
|
||||
Extensions ExtensionList `tls:"head=2"`
|
||||
}
|
||||
|
||||
101
vendor/github.com/bifurcation/mint/mint.svg
generated
vendored
101
vendor/github.com/bifurcation/mint/mint.svg
generated
vendored
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 16 KiB |
6
vendor/github.com/bifurcation/mint/negotiation.go
generated
vendored
6
vendor/github.com/bifurcation/mint/negotiation.go
generated
vendored
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
func VersionNegotiation(offered, supported []uint16) (bool, uint16) {
|
||||
for _, offeredVersion := range offered {
|
||||
for _, supportedVersion := range supported {
|
||||
logf(logTypeHandshake, "[server] version offered by client [%04x] <> [%04x]", offeredVersion, supportedVersion)
|
||||
if offeredVersion == supportedVersion {
|
||||
for _, tls13Version := range supported {
|
||||
logf(logTypeHandshake, "[server] version offered by client [%04x] <> [%04x]", offeredVersion, tls13Version)
|
||||
if offeredVersion == tls13Version {
|
||||
// XXX: Should probably be highest supported version, but for now, we
|
||||
// only support one version, so it doesn't really matter.
|
||||
return true, offeredVersion
|
||||
|
||||
161
vendor/github.com/bifurcation/mint/record-layer.go
generated
vendored
161
vendor/github.com/bifurcation/mint/record-layer.go
generated
vendored
@@ -20,11 +20,11 @@ func (err DecryptError) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
type direction uint8
|
||||
type Direction uint8
|
||||
|
||||
const (
|
||||
directionWrite = direction(1)
|
||||
directionRead = direction(2)
|
||||
DirectionWrite = Direction(1)
|
||||
DirectionRead = Direction(2)
|
||||
)
|
||||
|
||||
// struct {
|
||||
@@ -42,6 +42,18 @@ type TLSPlaintext struct {
|
||||
fragment []byte
|
||||
}
|
||||
|
||||
func NewTLSPlaintext(ct RecordType, epoch Epoch, fragment []byte) *TLSPlaintext {
|
||||
return &TLSPlaintext{
|
||||
contentType: ct,
|
||||
epoch: epoch,
|
||||
fragment: fragment,
|
||||
}
|
||||
}
|
||||
|
||||
func (t TLSPlaintext) Fragment() []byte {
|
||||
return t.fragment
|
||||
}
|
||||
|
||||
type cipherState struct {
|
||||
epoch Epoch // DTLS epoch
|
||||
ivLength int // Length of the seq and nonce fields
|
||||
@@ -50,10 +62,28 @@ type cipherState struct {
|
||||
cipher cipher.AEAD // AEAD cipher
|
||||
}
|
||||
|
||||
type RecordLayer struct {
|
||||
type RecordLayerFactory interface {
|
||||
NewLayer(conn io.ReadWriter, dir Direction) RecordLayer
|
||||
}
|
||||
|
||||
type RecordLayer interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
SetVersion(v uint16)
|
||||
SetLabel(s string)
|
||||
Rekey(epoch Epoch, factory AeadFactory, keys *KeySet) error
|
||||
ResetClear(seq uint64)
|
||||
DiscardReadKey(epoch Epoch)
|
||||
PeekRecordType(block bool) (RecordType, error)
|
||||
ReadRecord() (*TLSPlaintext, error)
|
||||
WriteRecord(pt *TLSPlaintext) error
|
||||
Epoch() Epoch
|
||||
}
|
||||
|
||||
type RecordLayerImpl struct {
|
||||
sync.Mutex
|
||||
label string
|
||||
direction direction
|
||||
direction Direction
|
||||
version uint16 // The current version number
|
||||
conn io.ReadWriter // The underlying connection
|
||||
frame *frameReader // The buffered frame reader
|
||||
@@ -67,6 +97,10 @@ type RecordLayer struct {
|
||||
datagram bool
|
||||
}
|
||||
|
||||
func (r *RecordLayerImpl) Impl() *RecordLayerImpl {
|
||||
return r
|
||||
}
|
||||
|
||||
type recordLayerFrameDetails struct {
|
||||
datagram bool
|
||||
}
|
||||
@@ -90,7 +124,7 @@ func newCipherStateNull() *cipherState {
|
||||
return &cipherState{EpochClear, 0, 0, nil, nil}
|
||||
}
|
||||
|
||||
func newCipherStateAead(epoch Epoch, factory aeadFactory, key []byte, iv []byte) (*cipherState, error) {
|
||||
func newCipherStateAead(epoch Epoch, factory AeadFactory, key []byte, iv []byte) (*cipherState, error) {
|
||||
cipher, err := factory(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -99,8 +133,8 @@ func newCipherStateAead(epoch Epoch, factory aeadFactory, key []byte, iv []byte)
|
||||
return &cipherState{epoch, len(iv), 0, iv, cipher}, nil
|
||||
}
|
||||
|
||||
func NewRecordLayerTLS(conn io.ReadWriter, dir direction) *RecordLayer {
|
||||
r := RecordLayer{}
|
||||
func NewRecordLayerTLS(conn io.ReadWriter, dir Direction) *RecordLayerImpl {
|
||||
r := RecordLayerImpl{}
|
||||
r.label = ""
|
||||
r.direction = dir
|
||||
r.conn = conn
|
||||
@@ -110,8 +144,8 @@ func NewRecordLayerTLS(conn io.ReadWriter, dir direction) *RecordLayer {
|
||||
return &r
|
||||
}
|
||||
|
||||
func NewRecordLayerDTLS(conn io.ReadWriter, dir direction) *RecordLayer {
|
||||
r := RecordLayer{}
|
||||
func NewRecordLayerDTLS(conn io.ReadWriter, dir Direction) *RecordLayerImpl {
|
||||
r := RecordLayerImpl{}
|
||||
r.label = ""
|
||||
r.direction = dir
|
||||
r.conn = conn
|
||||
@@ -123,29 +157,37 @@ func NewRecordLayerDTLS(conn io.ReadWriter, dir direction) *RecordLayer {
|
||||
return &r
|
||||
}
|
||||
|
||||
func (r *RecordLayer) SetVersion(v uint16) {
|
||||
func (r *RecordLayerImpl) SetVersion(v uint16) {
|
||||
r.version = v
|
||||
}
|
||||
|
||||
func (r *RecordLayer) ResetClear(seq uint64) {
|
||||
func (r *RecordLayerImpl) ResetClear(seq uint64) {
|
||||
r.cipher = newCipherStateNull()
|
||||
r.cipher.seq = seq
|
||||
}
|
||||
|
||||
func (r *RecordLayer) Rekey(epoch Epoch, factory aeadFactory, key []byte, iv []byte) error {
|
||||
cipher, err := newCipherStateAead(epoch, factory, key, iv)
|
||||
func (r *RecordLayerImpl) Epoch() Epoch {
|
||||
return r.cipher.epoch
|
||||
}
|
||||
|
||||
func (r *RecordLayerImpl) SetLabel(s string) {
|
||||
r.label = s
|
||||
}
|
||||
|
||||
func (r *RecordLayerImpl) Rekey(epoch Epoch, factory AeadFactory, keys *KeySet) error {
|
||||
cipher, err := newCipherStateAead(epoch, factory, keys.Key, keys.Iv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.cipher = cipher
|
||||
if r.datagram && r.direction == directionRead {
|
||||
if r.datagram && r.direction == DirectionRead {
|
||||
r.readCiphers[epoch] = cipher
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(ekr@rtfm.com): This is never used, which is a bug.
|
||||
func (r *RecordLayer) DiscardReadKey(epoch Epoch) {
|
||||
func (r *RecordLayerImpl) DiscardReadKey(epoch Epoch) {
|
||||
if !r.datagram {
|
||||
return
|
||||
}
|
||||
@@ -197,34 +239,29 @@ func (c *cipherState) overhead() int {
|
||||
return c.cipher.Overhead()
|
||||
}
|
||||
|
||||
func (r *RecordLayer) encrypt(cipher *cipherState, seq uint64, pt *TLSPlaintext, padLen int) *TLSPlaintext {
|
||||
assert(r.direction == directionWrite)
|
||||
func (r *RecordLayerImpl) encrypt(cipher *cipherState, seq uint64, header []byte, pt *TLSPlaintext, padLen int) []byte {
|
||||
assert(r.direction == DirectionWrite)
|
||||
logf(logTypeIO, "%s Encrypt seq=[%x]", r.label, seq)
|
||||
// Expand the fragment to hold contentType, padding, and overhead
|
||||
originalLen := len(pt.fragment)
|
||||
plaintextLen := originalLen + 1 + padLen
|
||||
ciphertextLen := plaintextLen + cipher.overhead()
|
||||
|
||||
// Assemble the revised plaintext
|
||||
out := &TLSPlaintext{
|
||||
|
||||
contentType: RecordTypeApplicationData,
|
||||
fragment: make([]byte, ciphertextLen),
|
||||
}
|
||||
copy(out.fragment, pt.fragment)
|
||||
out.fragment[originalLen] = byte(pt.contentType)
|
||||
ciphertext := make([]byte, ciphertextLen)
|
||||
copy(ciphertext, pt.fragment)
|
||||
ciphertext[originalLen] = byte(pt.contentType)
|
||||
for i := 1; i <= padLen; i++ {
|
||||
out.fragment[originalLen+i] = 0
|
||||
ciphertext[originalLen+i] = 0
|
||||
}
|
||||
|
||||
// Encrypt the fragment
|
||||
payload := out.fragment[:plaintextLen]
|
||||
cipher.cipher.Seal(payload[:0], cipher.computeNonce(seq), payload, nil)
|
||||
return out
|
||||
payload := ciphertext[:plaintextLen]
|
||||
cipher.cipher.Seal(payload[:0], cipher.computeNonce(seq), payload, header)
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
func (r *RecordLayer) decrypt(pt *TLSPlaintext, seq uint64) (*TLSPlaintext, int, error) {
|
||||
assert(r.direction == directionRead)
|
||||
func (r *RecordLayerImpl) decrypt(seq uint64, header []byte, pt *TLSPlaintext) (*TLSPlaintext, int, error) {
|
||||
assert(r.direction == DirectionRead)
|
||||
logf(logTypeIO, "%s Decrypt seq=[%x]", r.label, seq)
|
||||
if len(pt.fragment) < r.cipher.overhead() {
|
||||
msg := fmt.Sprintf("tls.record.decrypt: Record too short [%d] < [%d]", len(pt.fragment), r.cipher.overhead())
|
||||
@@ -238,7 +275,7 @@ func (r *RecordLayer) decrypt(pt *TLSPlaintext, seq uint64) (*TLSPlaintext, int,
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
_, err := r.cipher.cipher.Open(out.fragment[:0], r.cipher.computeNonce(seq), pt.fragment, nil)
|
||||
_, err := r.cipher.cipher.Open(out.fragment[:0], r.cipher.computeNonce(seq), pt.fragment, header)
|
||||
if err != nil {
|
||||
logf(logTypeIO, "%s AEAD decryption failure [%x]", r.label, pt)
|
||||
return nil, 0, DecryptError("tls.record.decrypt: AEAD decrypt failed")
|
||||
@@ -259,7 +296,7 @@ func (r *RecordLayer) decrypt(pt *TLSPlaintext, seq uint64) (*TLSPlaintext, int,
|
||||
return out, padLen, nil
|
||||
}
|
||||
|
||||
func (r *RecordLayer) PeekRecordType(block bool) (RecordType, error) {
|
||||
func (r *RecordLayerImpl) PeekRecordType(block bool) (RecordType, error) {
|
||||
var pt *TLSPlaintext
|
||||
var err error
|
||||
|
||||
@@ -275,7 +312,7 @@ func (r *RecordLayer) PeekRecordType(block bool) (RecordType, error) {
|
||||
return pt.contentType, nil
|
||||
}
|
||||
|
||||
func (r *RecordLayer) ReadRecord() (*TLSPlaintext, error) {
|
||||
func (r *RecordLayerImpl) ReadRecord() (*TLSPlaintext, error) {
|
||||
pt, err := r.nextRecord(false)
|
||||
|
||||
// Consume the cached record if there was one
|
||||
@@ -285,7 +322,7 @@ func (r *RecordLayer) ReadRecord() (*TLSPlaintext, error) {
|
||||
return pt, err
|
||||
}
|
||||
|
||||
func (r *RecordLayer) readRecordAnyEpoch() (*TLSPlaintext, error) {
|
||||
func (r *RecordLayerImpl) ReadRecordAnyEpoch() (*TLSPlaintext, error) {
|
||||
pt, err := r.nextRecord(true)
|
||||
|
||||
// Consume the cached record if there was one
|
||||
@@ -295,7 +332,7 @@ func (r *RecordLayer) readRecordAnyEpoch() (*TLSPlaintext, error) {
|
||||
return pt, err
|
||||
}
|
||||
|
||||
func (r *RecordLayer) nextRecord(allowOldEpoch bool) (*TLSPlaintext, error) {
|
||||
func (r *RecordLayerImpl) nextRecord(allowOldEpoch bool) (*TLSPlaintext, error) {
|
||||
cipher := r.cipher
|
||||
if r.cachedRecord != nil {
|
||||
logf(logTypeIO, "%s Returning cached record", r.label)
|
||||
@@ -391,7 +428,7 @@ func (r *RecordLayer) nextRecord(allowOldEpoch bool) (*TLSPlaintext, error) {
|
||||
|
||||
if cipher.cipher != nil {
|
||||
logf(logTypeIO, "%s RecordLayer.ReadRecord epoch=[%s] seq=[%x] [%d] ciphertext=[%x]", r.label, cipher.epoch.label(), seq, pt.contentType, pt.fragment)
|
||||
pt, _, err = r.decrypt(pt, seq)
|
||||
pt, _, err = r.decrypt(seq, header, pt)
|
||||
if err != nil {
|
||||
logf(logTypeIO, "%s Decryption failed", r.label)
|
||||
return nil, err
|
||||
@@ -411,46 +448,58 @@ func (r *RecordLayer) nextRecord(allowOldEpoch bool) (*TLSPlaintext, error) {
|
||||
return pt, nil
|
||||
}
|
||||
|
||||
func (r *RecordLayer) WriteRecord(pt *TLSPlaintext) error {
|
||||
func (r *RecordLayerImpl) WriteRecord(pt *TLSPlaintext) error {
|
||||
return r.writeRecordWithPadding(pt, r.cipher, 0)
|
||||
}
|
||||
|
||||
func (r *RecordLayer) WriteRecordWithPadding(pt *TLSPlaintext, padLen int) error {
|
||||
func (r *RecordLayerImpl) WriteRecordWithPadding(pt *TLSPlaintext, padLen int) error {
|
||||
return r.writeRecordWithPadding(pt, r.cipher, padLen)
|
||||
}
|
||||
|
||||
func (r *RecordLayer) writeRecordWithPadding(pt *TLSPlaintext, cipher *cipherState, padLen int) error {
|
||||
func (r *RecordLayerImpl) writeRecordWithPadding(pt *TLSPlaintext, cipher *cipherState, padLen int) error {
|
||||
seq := cipher.combineSeq(r.datagram)
|
||||
if cipher.cipher != nil {
|
||||
logf(logTypeIO, "%s RecordLayer.WriteRecord epoch=[%s] seq=[%x] [%d] plaintext=[%x]", r.label, cipher.epoch.label(), cipher.seq, pt.contentType, pt.fragment)
|
||||
pt = r.encrypt(cipher, seq, pt, padLen)
|
||||
} else if padLen > 0 {
|
||||
return fmt.Errorf("tls.record: Padding can only be done on encrypted records")
|
||||
}
|
||||
|
||||
if len(pt.fragment) > maxFragmentLen {
|
||||
return fmt.Errorf("tls.record: Record size too big")
|
||||
}
|
||||
|
||||
length := len(pt.fragment)
|
||||
var contentType RecordType
|
||||
if cipher.cipher != nil {
|
||||
length += 1 + padLen + cipher.cipher.Overhead()
|
||||
contentType = RecordTypeApplicationData
|
||||
} else {
|
||||
contentType = pt.contentType
|
||||
}
|
||||
var header []byte
|
||||
|
||||
if !r.datagram {
|
||||
header = []byte{byte(pt.contentType),
|
||||
header = []byte{byte(contentType),
|
||||
byte(r.version >> 8), byte(r.version & 0xff),
|
||||
byte(length >> 8), byte(length)}
|
||||
} else {
|
||||
header = make([]byte, 13)
|
||||
version := dtlsConvertVersion(r.version)
|
||||
copy(header, []byte{byte(pt.contentType),
|
||||
copy(header, []byte{byte(contentType),
|
||||
byte(version >> 8), byte(version & 0xff),
|
||||
})
|
||||
encodeUint(seq, 8, header[3:])
|
||||
encodeUint(uint64(length), 2, header[11:])
|
||||
}
|
||||
record := append(header, pt.fragment...)
|
||||
|
||||
logf(logTypeIO, "%s RecordLayer.WriteRecord epoch=[%s] seq=[%x] [%d] ciphertext=[%x]", r.label, cipher.epoch.label(), cipher.seq, pt.contentType, pt.fragment)
|
||||
var ciphertext []byte
|
||||
if cipher.cipher != nil {
|
||||
logf(logTypeIO, "%s RecordLayer.WriteRecord epoch=[%s] seq=[%x] [%d] plaintext=[%x]", r.label, cipher.epoch.label(), cipher.seq, pt.contentType, pt.fragment)
|
||||
ciphertext = r.encrypt(cipher, seq, header, pt, padLen)
|
||||
} else {
|
||||
if padLen > 0 {
|
||||
return fmt.Errorf("tls.record: Padding can only be done on encrypted records")
|
||||
}
|
||||
ciphertext = pt.fragment
|
||||
}
|
||||
|
||||
if len(ciphertext) > maxFragmentLen {
|
||||
return fmt.Errorf("tls.record: Record size too big")
|
||||
}
|
||||
|
||||
record := append(header, ciphertext...)
|
||||
|
||||
logf(logTypeIO, "%s RecordLayer.WriteRecord epoch=[%s] seq=[%x] [%d] ciphertext=[%x]", r.label, cipher.epoch.label(), cipher.seq, contentType, ciphertext)
|
||||
|
||||
cipher.incrementSequenceNumber()
|
||||
_, err := r.conn.Write(record)
|
||||
|
||||
8
vendor/github.com/bifurcation/mint/server-state-machine.go
generated
vendored
8
vendor/github.com/bifurcation/mint/server-state-machine.go
generated
vendored
@@ -163,7 +163,7 @@ func (state serverStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
|
||||
logf(logTypeHandshake, "[ServerStateStart] Client did not send supported_versions")
|
||||
return nil, nil, AlertProtocolVersion
|
||||
}
|
||||
versionOK, _ := VersionNegotiation(supportedVersions.Versions, []uint16{supportedVersion})
|
||||
versionOK, _ := VersionNegotiation(supportedVersions.Versions, []uint16{tls13Version})
|
||||
if !versionOK {
|
||||
logf(logTypeHandshake, "[ServerStateStart] Client does not support the same version")
|
||||
return nil, nil, AlertProtocolVersion
|
||||
@@ -416,7 +416,7 @@ func (state *serverStateStart) generateHRR(cs CipherSuite, legacySessionId []byt
|
||||
|
||||
sv := &SupportedVersionsExtension{
|
||||
HandshakeType: HandshakeTypeServerHello,
|
||||
Versions: []uint16{supportedVersion},
|
||||
Versions: []uint16{tls13Version},
|
||||
}
|
||||
|
||||
if err := hrr.Extensions.Add(sv); err != nil {
|
||||
@@ -483,7 +483,7 @@ func (state serverStateNegotiated) Next(_ handshakeMessageReader) (HandshakeStat
|
||||
|
||||
err := sh.Extensions.Add(&SupportedVersionsExtension{
|
||||
HandshakeType: HandshakeTypeServerHello,
|
||||
Versions: []uint16{supportedVersion},
|
||||
Versions: []uint16{tls13Version},
|
||||
})
|
||||
if err != nil {
|
||||
logf(logTypeHandshake, "[ServerStateNegotiated] Error adding supported_versions extension [%v]", err)
|
||||
@@ -785,7 +785,7 @@ func (state serverStateWaitEOED) State() State {
|
||||
func (state serverStateWaitEOED) Next(hr handshakeMessageReader) (HandshakeState, []HandshakeAction, Alert) {
|
||||
for {
|
||||
logf(logTypeHandshake, "Server reading early data...")
|
||||
assert(state.hsCtx.hIn.conn.cipher.epoch == EpochEarlyData)
|
||||
assert(state.hsCtx.hIn.conn.Epoch() == EpochEarlyData)
|
||||
t, err := state.hsCtx.hIn.conn.PeekRecordType(!state.hsCtx.hIn.nonblocking)
|
||||
if err == AlertWouldBlock {
|
||||
return nil, nil, AlertWouldBlock
|
||||
|
||||
8
vendor/github.com/bifurcation/mint/state-machine.go
generated
vendored
8
vendor/github.com/bifurcation/mint/state-machine.go
generated
vendored
@@ -19,12 +19,12 @@ type SendEarlyData struct{}
|
||||
|
||||
type RekeyIn struct {
|
||||
epoch Epoch
|
||||
KeySet keySet
|
||||
KeySet KeySet
|
||||
}
|
||||
|
||||
type RekeyOut struct {
|
||||
epoch Epoch
|
||||
KeySet keySet
|
||||
KeySet KeySet
|
||||
}
|
||||
|
||||
type ResetOut struct {
|
||||
@@ -111,7 +111,7 @@ func (state stateConnected) State() State {
|
||||
}
|
||||
|
||||
func (state *stateConnected) KeyUpdate(request KeyUpdateRequest) ([]HandshakeAction, Alert) {
|
||||
var trafficKeys keySet
|
||||
var trafficKeys KeySet
|
||||
if state.isClient {
|
||||
state.clientTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.clientTrafficSecret,
|
||||
labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size())
|
||||
@@ -196,7 +196,7 @@ func (state stateConnected) ProcessMessage(hm *HandshakeMessage) (HandshakeState
|
||||
|
||||
switch body := bodyGeneric.(type) {
|
||||
case *KeyUpdateBody:
|
||||
var trafficKeys keySet
|
||||
var trafficKeys KeySet
|
||||
if !state.isClient {
|
||||
state.clientTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.clientTrafficSecret,
|
||||
labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size())
|
||||
|
||||
84
vendor/github.com/bifurcation/mint/syntax/README.md
generated
vendored
84
vendor/github.com/bifurcation/mint/syntax/README.md
generated
vendored
@@ -1,84 +0,0 @@
|
||||
TLS Syntax
|
||||
==========
|
||||
|
||||
TLS defines [its own syntax](https://tlswg.github.io/tls13-spec/#rfc.section.3)
|
||||
for describing structures used in that protocol. To facilitate experimentation
|
||||
with TLS in Go, this module maps that syntax to the Go structure syntax, taking
|
||||
advantage of Go's type annotations to encode non-type information carried in the
|
||||
TLS presentation format.
|
||||
|
||||
For example, in the TLS specification, a ClientHello message has the following
|
||||
structure:
|
||||
|
||||
~~~~~
|
||||
uint16 ProtocolVersion;
|
||||
opaque Random[32];
|
||||
uint8 CipherSuite[2];
|
||||
enum { ... (65535)} ExtensionType;
|
||||
|
||||
struct {
|
||||
ExtensionType extension_type;
|
||||
opaque extension_data<0..2^16-1>;
|
||||
} Extension;
|
||||
|
||||
struct {
|
||||
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
|
||||
Random random;
|
||||
opaque legacy_session_id<0..32>;
|
||||
CipherSuite cipher_suites<2..2^16-2>;
|
||||
opaque legacy_compression_methods<1..2^8-1>;
|
||||
Extension extensions<0..2^16-1>;
|
||||
} ClientHello;
|
||||
~~~~~
|
||||
|
||||
This maps to the following Go type definitions:
|
||||
|
||||
~~~~~
|
||||
type protocolVersion uint16
|
||||
type random [32]byte
|
||||
type cipherSuite uint16 // or [2]byte
|
||||
type extensionType uint16
|
||||
|
||||
type extension struct {
|
||||
ExtensionType extensionType
|
||||
ExtensionData []byte `tls:"head=2"`
|
||||
}
|
||||
|
||||
type clientHello struct {
|
||||
LegacyVersion protocolVersion
|
||||
Random random
|
||||
LegacySessionID []byte `tls:"head=1,max=32"`
|
||||
CipherSuites []cipherSuite `tls:"head=2,min=2"`
|
||||
LegacyCompressionMethods []byte `tls:"head=1,min=1"`
|
||||
Extensions []extension `tls:"head=2"`
|
||||
}
|
||||
~~~~~
|
||||
|
||||
Then you can just declare, marshal, and unmarshal structs just like you would
|
||||
with, say JSON.
|
||||
|
||||
The available annotations right now are all related to vectors:
|
||||
|
||||
* `head`: The number of bytes of length to use as a "header"
|
||||
* `min`: The minimum length of the vector, in bytes
|
||||
* `max`: The maximum length of the vector, in bytes
|
||||
|
||||
## Not supported
|
||||
|
||||
* The `select()` syntax for creating alternate version of the same struct (see,
|
||||
e.g., the KeyShare extension)
|
||||
|
||||
* The backreference syntax for array lengths or select parameters, as in `opaque
|
||||
fragment[TLSPlaintext.length]`. Note, however, that in cases where the length
|
||||
immediately preceds the array, these can be reframed as vectors with
|
||||
appropriate sizes.
|
||||
|
||||
|
||||
QUIC Extensions Syntax
|
||||
======================
|
||||
syntax also supports some minor extensions to allow implementing QUIC.
|
||||
|
||||
* The `varint` annotation describes a QUIC-style varint
|
||||
* `head=none` means no header, i.e., the bytes are encoded directly on the wire.
|
||||
On reading, the decoder will consume all available data.
|
||||
* `head=varint` means to encode the header as a varint
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user