This is a huge commit, it does following things:
1. refactor kubesphere dependency service client creation, we can disable dependency by config 2. dependencies can be configured by configuration file 3. refactor cmd package using cobra.Command, so we can use hypersphere to invoke command sepearately. Later we only need to build one image to contains all kubesphere core components. One command to rule them all! 4. live reloading configuration currently not implemented
This commit is contained in:
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 admin_jenkins
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/golang/glog"
|
||||
"kubesphere.io/kubesphere/pkg/gojenkins"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
jenkinsInitMutex sync.Mutex
|
||||
jenkinsClient *gojenkins.Jenkins
|
||||
jenkinsAdminAddress string
|
||||
jenkinsAdminUsername string
|
||||
jenkinsAdminPassword string
|
||||
jenkinsMaxConn int
|
||||
)
|
||||
|
||||
const (
|
||||
JenkinsAllUserRoleName = "kubesphere-user"
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&jenkinsAdminAddress, "jenkins-address", "http://ks-jenkins.kubesphere-devops-system.svc/", "data source name")
|
||||
flag.StringVar(&jenkinsAdminUsername, "jenkins-username", "admin", "username of jenkins")
|
||||
flag.StringVar(&jenkinsAdminPassword, "jenkins-password", "passw0rd", "password of jenkins")
|
||||
flag.IntVar(&jenkinsMaxConn, "jenkins-max-conn", 20, "max conn to jenkins")
|
||||
}
|
||||
|
||||
func GetJenkins() *gojenkins.Jenkins {
|
||||
jenkins := gojenkins.CreateJenkins(nil, jenkinsAdminAddress, jenkinsMaxConn, jenkinsAdminUsername, jenkinsAdminPassword)
|
||||
return jenkins
|
||||
}
|
||||
|
||||
func Client() *gojenkins.Jenkins {
|
||||
if jenkinsClient == nil {
|
||||
jenkinsInitMutex.Lock()
|
||||
defer jenkinsInitMutex.Unlock()
|
||||
if jenkinsClient == nil {
|
||||
jenkins := GetJenkins()
|
||||
jenkins, err := jenkins.Init()
|
||||
if err != nil {
|
||||
glog.Errorf("failed to connect jenkins, %+v", err)
|
||||
return nil
|
||||
}
|
||||
globalRole, err := jenkins.GetGlobalRole(JenkinsAllUserRoleName)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get jenkins role, %+v", err)
|
||||
return nil
|
||||
}
|
||||
if globalRole == nil {
|
||||
_, err := jenkins.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{
|
||||
GlobalRead: true,
|
||||
}, true)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to create jenkins global role, %+v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
_, err = jenkins.AddProjectRole(JenkinsAllUserRoleName, "\\n\\s*\\r", gojenkins.ProjectPermissionIds{
|
||||
SCMTag: true,
|
||||
}, true)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to create jenkins project role, %+v", err)
|
||||
return nil
|
||||
}
|
||||
jenkinsClient = jenkins
|
||||
}
|
||||
}
|
||||
|
||||
return jenkinsClient
|
||||
|
||||
}
|
||||
109
pkg/simple/client/devops/devops.go
Normal file
109
pkg/simple/client/devops/devops.go
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2018 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 devops
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/gojenkins"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
JenkinsAllUserRoleName = "kubesphere-user"
|
||||
)
|
||||
|
||||
type DevopsClient struct {
|
||||
jenkinsClient *gojenkins.Jenkins
|
||||
}
|
||||
|
||||
func NewDevopsClient(options *DevopsOptions) (*DevopsClient, error) {
|
||||
var d DevopsClient
|
||||
|
||||
jenkins := gojenkins.CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password)
|
||||
jenkins, err := jenkins.Init()
|
||||
if err != nil {
|
||||
klog.Errorf("failed to connecto to jenkins role, %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.jenkinsClient = jenkins
|
||||
|
||||
err = d.initializeJenkins()
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
func NewDevopsClientOrDie(options *DevopsOptions) *DevopsClient {
|
||||
jenkins := gojenkins.CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password)
|
||||
jenkins, err := jenkins.Init()
|
||||
if err != nil {
|
||||
klog.Errorf("failed to connecto to jenkins role, %+v", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
d := &DevopsClient{
|
||||
jenkinsClient: jenkins,
|
||||
}
|
||||
|
||||
err = d.initializeJenkins()
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (c *DevopsClient) Jenkins() *gojenkins.Jenkins {
|
||||
return c.jenkinsClient
|
||||
}
|
||||
|
||||
var mutex = sync.Mutex{}
|
||||
|
||||
func (c *DevopsClient) initializeJenkins() error {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if c.jenkinsClient == nil {
|
||||
return fmt.Errorf("jenkins intialization failed")
|
||||
}
|
||||
|
||||
globalRole, err := c.jenkinsClient.GetGlobalRole(JenkinsAllUserRoleName)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Jenkins uninitialized, create global role
|
||||
if globalRole == nil {
|
||||
_, err := c.jenkinsClient.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{GlobalRead: true}, true)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = c.jenkinsClient.AddProjectRole(JenkinsAllUserRoleName, "\\n\\s*\\r", gojenkins.ProjectPermissionIds{SCMTag: true}, true)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
81
pkg/simple/client/devops/options.go
Normal file
81
pkg/simple/client/devops/options.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package devops
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type DevopsOptions struct {
|
||||
Host string `json:",omitempty" yaml:",omitempty" description:"Jenkins service host address"`
|
||||
Username string `json:",omitempty" yaml:",omitempty" description:"Jenkins admin username"`
|
||||
Password string `json:",omitempty" yaml:",omitempty" description:"Jenkins admin password"`
|
||||
MaxConnections int `json:"maxConnections,omitempty" yaml:"maxConnections,omitempty" description:"Maximum connections allowed to connect to Jenkins"`
|
||||
}
|
||||
|
||||
// NewDevopsOptions returns a `zero` instance
|
||||
func NewDevopsOptions() *DevopsOptions {
|
||||
return &DevopsOptions{
|
||||
Host: "",
|
||||
Username: "",
|
||||
Password: "",
|
||||
MaxConnections: 100,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DevopsOptions) ApplyTo(options *DevopsOptions) {
|
||||
if options == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.Host != "" {
|
||||
options.Host = s.Host
|
||||
}
|
||||
|
||||
if s.Username != "" {
|
||||
options.Username = s.Username
|
||||
}
|
||||
|
||||
if s.Password != "" {
|
||||
options.Password = s.Password
|
||||
}
|
||||
|
||||
if s.MaxConnections > 0 {
|
||||
options.MaxConnections = s.MaxConnections
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
func (s *DevopsOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
// devops is not needed, ignore rest options
|
||||
if s.Host == "" {
|
||||
return errors
|
||||
}
|
||||
|
||||
if s.Username == "" || s.Password == "" {
|
||||
errors = append(errors, fmt.Errorf("jenkins's username or password is empty"))
|
||||
}
|
||||
|
||||
if s.MaxConnections <= 0 {
|
||||
errors = append(errors, fmt.Errorf("jenkins's maximum connections should be greater than 0"))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (s *DevopsOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.Host, "jenkins-host", s.Host, ""+
|
||||
"Jenkins service host address. If left blank, means Jenkins "+
|
||||
"is unnecessary.")
|
||||
|
||||
fs.StringVar(&s.Username, "jenkins-username", s.Username, ""+
|
||||
"Username for access to Jenkins service. Leave it blank if there isn't any.")
|
||||
|
||||
fs.StringVar(&s.Password, "jenkins-password", s.Password, ""+
|
||||
"Password for access to Jenkins service, used pair with username.")
|
||||
|
||||
fs.IntVar(&s.MaxConnections, "jenkins-max-connections", s.MaxConnections, ""+
|
||||
"Maximum allowed connections to Jenkins. ")
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 devops_mysql
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/gocraft/dbr"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/db"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
dbClientOnce sync.Once
|
||||
dsn string
|
||||
dbClient *db.Database
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&dsn, "devops-database-connection", "root:password@tcp(openpitrix-db.openpitrix-system.svc:3306)/devops", "data source name")
|
||||
}
|
||||
|
||||
var defaultEventReceiver = db.EventReceiver{}
|
||||
|
||||
func OpenDatabase() *db.Database {
|
||||
dbClientOnce.Do(func() {
|
||||
conn, err := dbr.Open("mysql", dsn+"?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", &defaultEventReceiver)
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
}
|
||||
conn.SetMaxIdleConns(100)
|
||||
conn.SetMaxOpenConns(100)
|
||||
conn.SetConnMaxLifetime(10 * time.Second)
|
||||
dbClient = &db.Database{
|
||||
Session: conn.NewSession(nil),
|
||||
}
|
||||
err = dbClient.Ping()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
})
|
||||
return dbClient
|
||||
}
|
||||
315
pkg/simple/client/factory.go
Normal file
315
pkg/simple/client/factory.go
Normal file
@@ -0,0 +1,315 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
goredis "github.com/go-redis/redis"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/devops"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/prometheus"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/redis"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/s2is3"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ClientSetNotEnabledError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e ClientSetNotEnabledError) Error() string {
|
||||
return fmt.Sprintf("client set not enabled: %s", e.err.Error())
|
||||
}
|
||||
|
||||
type ClientSetOptions struct {
|
||||
mySQLOptions *mysql.MySQLOptions
|
||||
redisOptions *redis.RedisOptions
|
||||
kubernetesOptions *k8s.KubernetesOptions
|
||||
devopsOptions *devops.DevopsOptions
|
||||
sonarqubeOptions *sonarqube.SonarQubeOptions
|
||||
ldapOptions *ldap.LdapOptions
|
||||
s3Options *s2is3.S3Options
|
||||
openPitrixOptions *openpitrix.OpenPitrixOptions
|
||||
prometheusOptions *prometheus.PrometheusOptions
|
||||
}
|
||||
|
||||
func NewClientSetOptions() *ClientSetOptions {
|
||||
return &ClientSetOptions{
|
||||
mySQLOptions: mysql.NewMySQLOptions(),
|
||||
redisOptions: redis.NewRedisOptions(),
|
||||
kubernetesOptions: k8s.NewKubernetesOptions(),
|
||||
ldapOptions: ldap.NewLdapOptions(),
|
||||
devopsOptions: devops.NewDevopsOptions(),
|
||||
sonarqubeOptions: sonarqube.NewSonarQubeOptions(),
|
||||
s3Options: s2is3.NewS3Options(),
|
||||
openPitrixOptions: openpitrix.NewOpenPitrixOptions(),
|
||||
prometheusOptions: prometheus.NewPrometheusOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetMySQLOptions(options *mysql.MySQLOptions) *ClientSetOptions {
|
||||
c.mySQLOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetRedisOptions(options *redis.RedisOptions) *ClientSetOptions {
|
||||
c.redisOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetKubernetesOptions(options *k8s.KubernetesOptions) *ClientSetOptions {
|
||||
c.kubernetesOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetDevopsOptions(options *devops.DevopsOptions) *ClientSetOptions {
|
||||
c.devopsOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetLdapOptions(options *ldap.LdapOptions) *ClientSetOptions {
|
||||
c.ldapOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetS3Options(options *s2is3.S3Options) *ClientSetOptions {
|
||||
c.s3Options = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetOpenPitrixOptions(options *openpitrix.OpenPitrixOptions) *ClientSetOptions {
|
||||
c.openPitrixOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClientSetOptions) SetPrometheusOptions(options *prometheus.PrometheusOptions) *ClientSetOptions {
|
||||
c.prometheusOptions = options
|
||||
return c
|
||||
}
|
||||
|
||||
// ClientSet provide best of effort service to initialize clients,
|
||||
// but there is no guarantee to return a valid client instance,
|
||||
// so do validity check before use
|
||||
type ClientSet struct {
|
||||
csoptions *ClientSetOptions
|
||||
stopCh <-chan struct{}
|
||||
|
||||
mySQLClient *mysql.MySQLClient
|
||||
|
||||
k8sClient *k8s.KubernetesClient
|
||||
ldapClient *ldap.LdapClient
|
||||
devopsClient *devops.DevopsClient
|
||||
sonarQubeClient *sonarqube.SonarQubeClient
|
||||
redisClient *redis.RedisClient
|
||||
s3Client *s2is3.S3Client
|
||||
prometheusClient *prometheus.PrometheusClient
|
||||
openpitrixClient *openpitrix.OpenPitrixClient
|
||||
}
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
// global clientsets instance
|
||||
var sharedClientSet *ClientSet
|
||||
|
||||
func ClientSets() *ClientSet {
|
||||
return sharedClientSet
|
||||
}
|
||||
|
||||
func NewClientSetFactory(c *ClientSetOptions, stopCh <-chan struct{}) *ClientSet {
|
||||
sharedClientSet = &ClientSet{csoptions: c, stopCh: stopCh}
|
||||
|
||||
if c.kubernetesOptions != nil {
|
||||
sharedClientSet.k8sClient = k8s.NewKubernetesClientOrDie(c.kubernetesOptions)
|
||||
}
|
||||
|
||||
return sharedClientSet
|
||||
}
|
||||
|
||||
// lazy creating
|
||||
func (cs *ClientSet) MySQL() (*mysql.Database, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.mySQLOptions == nil || cs.csoptions.mySQLOptions.Host == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.mySQLClient != nil {
|
||||
return cs.mySQLClient.Database(), nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
if cs.mySQLClient == nil {
|
||||
cs.mySQLClient, err = mysql.NewMySQLClient(cs.csoptions.mySQLOptions, cs.stopCh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cs.mySQLClient.Database(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientSet) Redis() (*goredis.Client, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.redisOptions == nil || cs.csoptions.redisOptions.Host == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.redisClient != nil {
|
||||
return cs.redisClient.Redis(), nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
if cs.redisClient == nil {
|
||||
cs.redisClient, err = redis.NewRedisClient(cs.csoptions.redisOptions, cs.stopCh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cs.redisClient.Redis(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientSet) Devops() (*devops.DevopsClient, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.devopsOptions == nil || cs.csoptions.devopsOptions.Host == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.devopsClient != nil {
|
||||
return cs.devopsClient, nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if cs.devopsClient == nil {
|
||||
cs.devopsClient, err = devops.NewDevopsClient(cs.csoptions.devopsOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cs.devopsClient, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientSet) SonarQube() (*sonarqube.SonarQubeClient, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.sonarqubeOptions == nil || cs.csoptions.sonarqubeOptions.Host == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.sonarQubeClient != nil {
|
||||
return cs.sonarQubeClient, nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if cs.sonarQubeClient == nil {
|
||||
cs.sonarQubeClient, err = sonarqube.NewSonarQubeClient(cs.csoptions.sonarqubeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cs.sonarQubeClient, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientSet) Ldap() (*ldap.LdapClient, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.ldapOptions == nil || cs.csoptions.ldapOptions.Host == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.ldapClient != nil {
|
||||
return cs.ldapClient, nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if cs.ldapClient == nil {
|
||||
cs.ldapClient, err = ldap.NewLdapClient(cs.csoptions.ldapOptions, cs.stopCh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cs.ldapClient, nil
|
||||
}
|
||||
}
|
||||
|
||||
// since kubernetes client is required, we will
|
||||
// create it on setup
|
||||
func (cs *ClientSet) K8s() *k8s.KubernetesClient {
|
||||
return cs.k8sClient
|
||||
}
|
||||
|
||||
func (cs *ClientSet) S3() (*s2is3.S3Client, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.s3Options == nil || cs.csoptions.s3Options.Endpoint == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.s3Client != nil {
|
||||
return cs.s3Client, nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if cs.s3Client == nil {
|
||||
cs.s3Client, err = s2is3.NewS3Client(cs.csoptions.s3Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cs.s3Client, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientSet) OpenPitrix() (*openpitrix.OpenPitrixClient, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.openPitrixOptions == nil || cs.csoptions.openPitrixOptions.APIServer == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.openpitrixClient != nil {
|
||||
return cs.openpitrixClient, nil
|
||||
} else {
|
||||
cs.openpitrixClient, err = openpitrix.NewOpenPitrixClient(cs.csoptions.openPitrixOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cs.openpitrixClient, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientSet) Prometheus() (*prometheus.PrometheusClient, error) {
|
||||
var err error
|
||||
|
||||
if cs.csoptions.prometheusOptions == nil || cs.csoptions.prometheusOptions.Endpoint == "" {
|
||||
return nil, ClientSetNotEnabledError{}
|
||||
}
|
||||
|
||||
if cs.prometheusClient != nil {
|
||||
return cs.prometheusClient, nil
|
||||
} else {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if cs.prometheusClient == nil {
|
||||
cs.prometheusClient, err = prometheus.NewPrometheusClient(cs.csoptions.prometheusOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cs.prometheusClient, nil
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
|
||||
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 k8s
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
kubeConfigFile string
|
||||
k8sClient *kubernetes.Clientset
|
||||
k8sClientOnce sync.Once
|
||||
KubeConfig *rest.Config
|
||||
MasterURL string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&kubeConfigFile, "kubeconfig-path", "", "path to kubeconfig file")
|
||||
flag.StringVar(&MasterURL, "master-url", "", "kube-apiserver url, only needed when out of cluster")
|
||||
}
|
||||
|
||||
func Client() *kubernetes.Clientset {
|
||||
|
||||
k8sClientOnce.Do(func() {
|
||||
|
||||
config, err := Config()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
k8sClient = kubernetes.NewForConfigOrDie(config)
|
||||
|
||||
KubeConfig = config
|
||||
})
|
||||
|
||||
return k8sClient
|
||||
}
|
||||
|
||||
func Config() (kubeConfig *rest.Config, err error) {
|
||||
|
||||
if _, err = os.Stat(kubeConfigFile); err == nil {
|
||||
kubeConfig, err = clientcmd.BuildConfigFromFlags(MasterURL, kubeConfigFile)
|
||||
} else {
|
||||
kubeConfig, err = rest.InClusterConfig()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kubeConfig.QPS = 1e6
|
||||
kubeConfig.Burst = 1e6
|
||||
|
||||
return kubeConfig, nil
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
|
||||
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 k8s
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
ks "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
var (
|
||||
ksClient *ks.Clientset
|
||||
ksClientOnce sync.Once
|
||||
)
|
||||
|
||||
func KsClient() *ks.Clientset {
|
||||
|
||||
ksClientOnce.Do(func() {
|
||||
|
||||
config, err := Config()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
ksClient = ks.NewForConfigOrDie(config)
|
||||
})
|
||||
|
||||
return ksClient
|
||||
}
|
||||
100
pkg/simple/client/k8s/kubernetes.go
Normal file
100
pkg/simple/client/k8s/kubernetes.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
s2i "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
type KubernetesClient struct {
|
||||
// kubernetes client interface
|
||||
k8s *kubernetes.Clientset
|
||||
|
||||
// generated clientset
|
||||
ks *kubesphere.Clientset
|
||||
|
||||
s2i *s2i.Clientset
|
||||
|
||||
master string
|
||||
|
||||
config *rest.Config
|
||||
}
|
||||
|
||||
// NewKubernetesClient
|
||||
func NewKubernetesClientOrDie(options *KubernetesOptions) *KubernetesClient {
|
||||
config, err := clientcmd.BuildConfigFromFlags("", options.KubeConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
config.QPS = options.QPS
|
||||
config.Burst = options.Burst
|
||||
|
||||
k := &KubernetesClient{
|
||||
k8s: kubernetes.NewForConfigOrDie(config),
|
||||
ks: kubesphere.NewForConfigOrDie(config),
|
||||
s2i: s2i.NewForConfigOrDie(config),
|
||||
master: config.Host,
|
||||
config: config,
|
||||
}
|
||||
|
||||
if options.Master != "" {
|
||||
k.master = options.Master
|
||||
}
|
||||
|
||||
return k
|
||||
}
|
||||
|
||||
func NewKubernetesClient(options *KubernetesOptions) (*KubernetesClient, error) {
|
||||
config, err := clientcmd.BuildConfigFromFlags("", options.KubeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.QPS = options.QPS
|
||||
config.Burst = options.Burst
|
||||
|
||||
var k KubernetesClient
|
||||
k.k8s, err = kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.ks, err = kubesphere.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.s2i, err = s2i.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.master = options.Master
|
||||
k.config = config
|
||||
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesClient) Kubernetes() kubernetes.Interface {
|
||||
return k.k8s
|
||||
}
|
||||
|
||||
func (k *KubernetesClient) KubeSphere() kubesphere.Interface {
|
||||
return k.ks
|
||||
}
|
||||
|
||||
func (k *KubernetesClient) S2i() s2i.Interface {
|
||||
return k.s2i
|
||||
}
|
||||
|
||||
// master address used to generate kubeconfig for downloading
|
||||
func (k *KubernetesClient) Master() string {
|
||||
return k.master
|
||||
}
|
||||
|
||||
func (k *KubernetesClient) Config() *rest.Config {
|
||||
return k.config
|
||||
}
|
||||
59
pkg/simple/client/k8s/options.go
Normal file
59
pkg/simple/client/k8s/options.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"os"
|
||||
)
|
||||
|
||||
type KubernetesOptions struct {
|
||||
// kubeconfig path, if not specified, will use
|
||||
// in cluster way to create clientset
|
||||
KubeConfig string `json:"kubeconfig" yaml:"kubeconfig"`
|
||||
|
||||
// kubernetes apiserver public address, used to generate kubeconfig
|
||||
// for downloading, default to host defined in kubeconfig
|
||||
// +optional
|
||||
Master string `json:"master,omitempty" yaml:"master,omitempty"`
|
||||
|
||||
// kubernetes clientset qps
|
||||
// +optional
|
||||
QPS float32 `json:"qps,omitemtpy" yaml:"qps,omitempty"`
|
||||
|
||||
// kubernetes clientset burst
|
||||
// +optional
|
||||
Burst int `json:"burst,omitempty" yaml:"burst,omitempty"`
|
||||
}
|
||||
|
||||
// NewKubernetesOptions returns a `zero` instance
|
||||
func NewKubernetesOptions() *KubernetesOptions {
|
||||
return &KubernetesOptions{
|
||||
KubeConfig: "",
|
||||
QPS: 1e6,
|
||||
Burst: 1e6,
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KubernetesOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
if k.KubeConfig != "" {
|
||||
if _, err := os.Stat(k.KubeConfig); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
func (k *KubernetesOptions) ApplyTo(options *KubernetesOptions) {
|
||||
reflectutils.Override(options, k)
|
||||
}
|
||||
|
||||
func (k *KubernetesOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&k.KubeConfig, "kubeconfig", k.KubeConfig, ""+
|
||||
"Path for kubernetes kubeconfig file, if left blank, will use "+
|
||||
"in cluster way.")
|
||||
|
||||
fs.StringVar(&k.Master, "master", k.Master, ""+
|
||||
"Used to generate kubeconfig for downloading, if not specified, will use host in kubeconfig.")
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
|
||||
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 k8s
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
s2i "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
var (
|
||||
s2iClient *s2i.Clientset
|
||||
s2iClientOnce sync.Once
|
||||
)
|
||||
|
||||
func S2iClient() *s2i.Clientset {
|
||||
|
||||
s2iClientOnce.Do(func() {
|
||||
|
||||
config, err := Config()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
s2iClient = s2i.NewForConfigOrDie(config)
|
||||
})
|
||||
|
||||
return s2iClient
|
||||
}
|
||||
1
pkg/simple/client/kubesphere/options.go
Normal file
1
pkg/simple/client/kubesphere/options.go
Normal file
@@ -0,0 +1 @@
|
||||
package kubesphere
|
||||
87
pkg/simple/client/ldap/ldap.go
Normal file
87
pkg/simple/client/ldap/ldap.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
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 ldap
|
||||
|
||||
import (
|
||||
"github.com/go-ldap/ldap"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
type LdapClient struct {
|
||||
pool Pool
|
||||
options *LdapOptions
|
||||
}
|
||||
|
||||
// panic if cannot connect to ldap service
|
||||
func NewLdapClient(options *LdapOptions, stopCh <-chan struct{}) (*LdapClient, error) {
|
||||
pool, err := NewChannelPool(8, 64, "kubesphere", func(s string) (ldap.Client, error) {
|
||||
conn, err := ldap.Dial("tcp", options.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}, []uint16{ldap.LDAPResultAdminLimitExceeded, ldap.ErrorNetwork})
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
pool.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &LdapClient{
|
||||
pool: pool,
|
||||
options: options,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-stopCh
|
||||
if client.pool != nil {
|
||||
client.pool.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (l *LdapClient) Ldap() ldap.Client {
|
||||
if l.pool != nil {
|
||||
conn, err := l.pool.Get()
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = conn.Bind(l.options.ManagerDN, l.options.ManagerPassword)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
klog.Error(err)
|
||||
return nil
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LdapClient) GroupSearchBase() string {
|
||||
return l.options.GroupSearchBase
|
||||
}
|
||||
|
||||
func (l *LdapClient) UserSearchBase() string {
|
||||
return l.options.UserSearchBase
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
|
||||
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 ldap
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/go-ldap/ldap"
|
||||
"github.com/golang/glog"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
pool Pool
|
||||
ldapHost string
|
||||
ManagerDN string
|
||||
ManagerPassword string
|
||||
UserSearchBase string
|
||||
GroupSearchBase string
|
||||
poolSize int
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&ldapHost, "ldap-server", "localhost:389", "ldap server host")
|
||||
flag.StringVar(&ManagerDN, "ldap-manager-dn", "cn=admin,dc=example,dc=org", "ldap manager dn")
|
||||
flag.StringVar(&ManagerPassword, "ldap-manager-password", "admin", "ldap manager password")
|
||||
flag.StringVar(&UserSearchBase, "ldap-user-search-base", "ou=Users,dc=example,dc=org", "ldap user search base")
|
||||
flag.StringVar(&GroupSearchBase, "ldap-group-search-base", "ou=Groups,dc=example,dc=org", "ldap group search base")
|
||||
flag.IntVar(&poolSize, "ldap-pool-size", 64, "ldap connection pool size")
|
||||
}
|
||||
|
||||
func ldapClientPool() Pool {
|
||||
|
||||
once.Do(func() {
|
||||
var err error
|
||||
pool, err = NewChannelPool(8, poolSize, "kubesphere", func(s string) (ldap.Client, error) {
|
||||
conn, err := ldap.Dial("tcp", ldapHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}, []uint16{ldap.LDAPResultTimeLimitExceeded, ldap.ErrorNetwork})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
})
|
||||
return pool
|
||||
}
|
||||
|
||||
func Client() (ldap.Client, error) {
|
||||
conn, err := ldapClientPool().Get()
|
||||
|
||||
if err != nil {
|
||||
glog.Errorln("get ldap connection from pool", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = conn.Bind(ManagerDN, ManagerPassword)
|
||||
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
glog.Errorln("bind manager dn", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
53
pkg/simple/client/ldap/options.go
Normal file
53
pkg/simple/client/ldap/options.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
)
|
||||
|
||||
type LdapOptions struct {
|
||||
Host string `json:"host,omitempty" yaml:"host,omitempty"`
|
||||
ManagerDN string `json:"managerDN,omitempty" yaml:"managerDN,omitempty"`
|
||||
ManagerPassword string `json:"managerPassword,omitempty" yaml:"managerPassword,omitempty"`
|
||||
UserSearchBase string `json:"userSearchBase,omitempty" yaml:"userSearchBase,omitempty"`
|
||||
GroupSearchBase string `json:"groupSearchBase,omitempty" yaml:"groupSearchBase,omitempty"`
|
||||
}
|
||||
|
||||
// NewLdapOptions return a default option
|
||||
// which host field point to nowhere.
|
||||
func NewLdapOptions() *LdapOptions {
|
||||
return &LdapOptions{
|
||||
Host: "",
|
||||
ManagerDN: "cn=admin,dc=example,dc=org",
|
||||
UserSearchBase: "ou=Users,dc=example,dc=org",
|
||||
GroupSearchBase: "ou=Groups,dc=example,dc=org",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LdapOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (l *LdapOptions) ApplyTo(options *LdapOptions) {
|
||||
reflectutils.Override(options, l)
|
||||
}
|
||||
|
||||
func (l *LdapOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&l.Host, "ldap-host", l.Host, ""+
|
||||
"Ldap service host, if left blank, all of the following options will "+
|
||||
"be ignored and ldap will be disabled.")
|
||||
|
||||
fs.StringVar(&l.ManagerDN, "ldap-manager-dn", l.ManagerDN, ""+
|
||||
"Ldap manager account domain name.")
|
||||
|
||||
fs.StringVar(&l.ManagerPassword, "ldap-manager-password", l.ManagerPassword, ""+
|
||||
"Ldap manager account password.")
|
||||
|
||||
fs.StringVar(&l.UserSearchBase, "ldap-user-search-base", l.UserSearchBase, ""+
|
||||
"Ldap user search base.")
|
||||
|
||||
fs.StringVar(&l.GroupSearchBase, "ldap-group-search-base", l.GroupSearchBase, ""+
|
||||
"Ldap group search base.")
|
||||
}
|
||||
109
pkg/simple/client/mysql/condition.go
Normal file
109
pkg/simple/client/mysql/condition.go
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2018 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 mysql
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gocraft/dbr"
|
||||
)
|
||||
|
||||
const (
|
||||
placeholder = "?"
|
||||
)
|
||||
|
||||
type EqCondition struct {
|
||||
dbr.Builder
|
||||
Column string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Copy From vendor/github.com/gocraft/dbr/condition.go:36
|
||||
func buildCmp(d dbr.Dialect, buf dbr.Buffer, pred string, column string, value interface{}) error {
|
||||
buf.WriteString(d.QuoteIdent(column))
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(pred)
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(placeholder)
|
||||
|
||||
buf.WriteValue(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// And creates AND from a list of conditions
|
||||
func And(cond ...dbr.Builder) dbr.Builder {
|
||||
return dbr.And(cond...)
|
||||
}
|
||||
|
||||
// Or creates OR from a list of conditions
|
||||
func Or(cond ...dbr.Builder) dbr.Builder {
|
||||
return dbr.Or(cond...)
|
||||
}
|
||||
|
||||
func escape(str string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
switch r {
|
||||
case '%', '\'', '^', '[', ']', '!', '_':
|
||||
return ' '
|
||||
}
|
||||
return r
|
||||
}, str)
|
||||
}
|
||||
|
||||
func Like(column string, value string) dbr.Builder {
|
||||
value = "%" + strings.TrimSpace(escape(value)) + "%"
|
||||
return dbr.BuildFunc(func(d dbr.Dialect, buf dbr.Buffer) error {
|
||||
return buildCmp(d, buf, "LIKE", column, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Eq is `=`.
|
||||
// When value is nil, it will be translated to `IS NULL`.
|
||||
// When value is a slice, it will be translated to `IN`.
|
||||
// Otherwise it will be translated to `=`.
|
||||
func Eq(column string, value interface{}) dbr.Builder {
|
||||
return &EqCondition{
|
||||
Builder: dbr.Eq(column, value),
|
||||
Column: column,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// Neq is `!=`.
|
||||
// When value is nil, it will be translated to `IS NOT NULL`.
|
||||
// When value is a slice, it will be translated to `NOT IN`.
|
||||
// Otherwise it will be translated to `!=`.
|
||||
func Neq(column string, value interface{}) dbr.Builder {
|
||||
return dbr.Neq(column, value)
|
||||
}
|
||||
|
||||
// Gt is `>`.
|
||||
func Gt(column string, value interface{}) dbr.Builder {
|
||||
return dbr.Gt(column, value)
|
||||
}
|
||||
|
||||
// Gte is '>='.
|
||||
func Gte(column string, value interface{}) dbr.Builder {
|
||||
return dbr.Gte(column, value)
|
||||
}
|
||||
|
||||
// Lt is '<'.
|
||||
func Lt(column string, value interface{}) dbr.Builder {
|
||||
return dbr.Lt(column, value)
|
||||
}
|
||||
|
||||
// Lte is `<=`.
|
||||
func Lte(column string, value interface{}) dbr.Builder {
|
||||
return dbr.Lte(column, value)
|
||||
}
|
||||
88
pkg/simple/client/mysql/condition_test.go
Normal file
88
pkg/simple/client/mysql/condition_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2018 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 mysql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gocraft/dbr"
|
||||
"github.com/gocraft/dbr/dialect"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Ref: https://github.com/gocraft/dbr/blob/5d59a8b3aa915660960329efb3af5513e7a0db07/condition_test.go
|
||||
func TestCondition(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
cond dbr.Builder
|
||||
query string
|
||||
value []interface{}
|
||||
}{
|
||||
{
|
||||
cond: Eq("col", 1),
|
||||
query: "`col` = ?",
|
||||
value: []interface{}{1},
|
||||
},
|
||||
{
|
||||
cond: Eq("col", nil),
|
||||
query: "`col` IS NULL",
|
||||
value: nil,
|
||||
},
|
||||
{
|
||||
cond: Eq("col", []int{}),
|
||||
query: "0",
|
||||
value: nil,
|
||||
},
|
||||
{
|
||||
cond: Neq("col", 1),
|
||||
query: "`col` != ?",
|
||||
value: []interface{}{1},
|
||||
},
|
||||
{
|
||||
cond: Neq("col", nil),
|
||||
query: "`col` IS NOT NULL",
|
||||
value: nil,
|
||||
},
|
||||
{
|
||||
cond: Gt("col", 1),
|
||||
query: "`col` > ?",
|
||||
value: []interface{}{1},
|
||||
},
|
||||
{
|
||||
cond: Gte("col", 1),
|
||||
query: "`col` >= ?",
|
||||
value: []interface{}{1},
|
||||
},
|
||||
{
|
||||
cond: Lt("col", 1),
|
||||
query: "`col` < ?",
|
||||
value: []interface{}{1},
|
||||
},
|
||||
{
|
||||
cond: Lte("col", 1),
|
||||
query: "`col` <= ?",
|
||||
value: []interface{}{1},
|
||||
},
|
||||
{
|
||||
cond: And(Lt("a", 1), Or(Gt("b", 2), Neq("c", 3))),
|
||||
query: "(`a` < ?) AND ((`b` > ?) OR (`c` != ?))",
|
||||
value: []interface{}{1, 2, 3},
|
||||
},
|
||||
} {
|
||||
buf := dbr.NewBuffer()
|
||||
err := test.cond.Build(dialect.MySQL, buf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.query, buf.String())
|
||||
assert.Equal(t, test.value, buf.Value())
|
||||
}
|
||||
}
|
||||
283
pkg/simple/client/mysql/db.go
Normal file
283
pkg/simple/client/mysql/db.go
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
Copyright 2018 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 mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/gocraft/dbr"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultSelectLimit = 200
|
||||
)
|
||||
|
||||
func GetLimit(n uint64) uint64 {
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
if n > DefaultSelectLimit {
|
||||
n = DefaultSelectLimit
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func GetOffset(n uint64) uint64 {
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
type InsertHook func(query *InsertQuery)
|
||||
type UpdateHook func(query *UpdateQuery)
|
||||
type DeleteHook func(query *DeleteQuery)
|
||||
|
||||
type Database struct {
|
||||
*dbr.Session
|
||||
InsertHook InsertHook
|
||||
UpdateHook UpdateHook
|
||||
DeleteHook DeleteHook
|
||||
}
|
||||
|
||||
type SelectQuery struct {
|
||||
*dbr.SelectBuilder
|
||||
JoinCount int // for join filter
|
||||
}
|
||||
|
||||
type InsertQuery struct {
|
||||
*dbr.InsertBuilder
|
||||
Hook InsertHook
|
||||
}
|
||||
|
||||
type DeleteQuery struct {
|
||||
*dbr.DeleteBuilder
|
||||
Hook DeleteHook
|
||||
}
|
||||
|
||||
type UpdateQuery struct {
|
||||
*dbr.UpdateBuilder
|
||||
Hook UpdateHook
|
||||
}
|
||||
|
||||
type UpsertQuery struct {
|
||||
table string
|
||||
*dbr.Session
|
||||
whereConds map[string]string
|
||||
upsertValues map[string]interface{}
|
||||
}
|
||||
|
||||
// SelectQuery
|
||||
// Example: Select().From().Where().Limit().Offset().OrderDir().Load()
|
||||
// Select().From().Where().Limit().Offset().OrderDir().LoadOne()
|
||||
// Select().From().Where().Count()
|
||||
// SelectAll().From().Where().Limit().Offset().OrderDir().Load()
|
||||
// SelectAll().From().Where().Limit().Offset().OrderDir().LoadOne()
|
||||
// SelectAll().From().Where().Count()
|
||||
|
||||
func (db *Database) Select(columns ...string) *SelectQuery {
|
||||
return &SelectQuery{db.Session.Select(columns...), 0}
|
||||
}
|
||||
|
||||
func (db *Database) SelectBySql(query string, value ...interface{}) *SelectQuery {
|
||||
return &SelectQuery{db.Session.SelectBySql(query, value...), 0}
|
||||
}
|
||||
|
||||
func (db *Database) SelectAll(columns ...string) *SelectQuery {
|
||||
return &SelectQuery{db.Session.Select("*"), 0}
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Join(table, on interface{}) *SelectQuery {
|
||||
b.SelectBuilder.Join(table, on)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) JoinAs(table string, alias string, on interface{}) *SelectQuery {
|
||||
b.SelectBuilder.Join(dbr.I(table).As(alias), on)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) From(table string) *SelectQuery {
|
||||
b.SelectBuilder.From(table)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Where(query interface{}, value ...interface{}) *SelectQuery {
|
||||
b.SelectBuilder.Where(query, value...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) GroupBy(col ...string) *SelectQuery {
|
||||
b.SelectBuilder.GroupBy(col...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Distinct() *SelectQuery {
|
||||
b.SelectBuilder.Distinct()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Limit(n uint64) *SelectQuery {
|
||||
n = GetLimit(n)
|
||||
b.SelectBuilder.Limit(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Offset(n uint64) *SelectQuery {
|
||||
n = GetLimit(n)
|
||||
b.SelectBuilder.Offset(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) OrderDir(col string, isAsc bool) *SelectQuery {
|
||||
b.SelectBuilder.OrderDir(col, isAsc)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Load(value interface{}) (int, error) {
|
||||
return b.SelectBuilder.Load(value)
|
||||
}
|
||||
|
||||
func (b *SelectQuery) LoadOne(value interface{}) error {
|
||||
return b.SelectBuilder.LoadOne(value)
|
||||
}
|
||||
|
||||
func getColumns(dbrColumns []interface{}) string {
|
||||
var columns []string
|
||||
for _, column := range dbrColumns {
|
||||
if c, ok := column.(string); ok {
|
||||
columns = append(columns, c)
|
||||
}
|
||||
}
|
||||
return strings.Join(columns, ", ")
|
||||
}
|
||||
|
||||
func (b *SelectQuery) Count() (count uint32, err error) {
|
||||
// cache SelectStmt
|
||||
selectStmt := b.SelectStmt
|
||||
|
||||
limit := selectStmt.LimitCount
|
||||
offset := selectStmt.OffsetCount
|
||||
column := selectStmt.Column
|
||||
isDistinct := selectStmt.IsDistinct
|
||||
order := selectStmt.Order
|
||||
|
||||
b.SelectStmt.LimitCount = -1
|
||||
b.SelectStmt.OffsetCount = -1
|
||||
b.SelectStmt.Column = []interface{}{"COUNT(*)"}
|
||||
b.SelectStmt.Order = []dbr.Builder{}
|
||||
|
||||
if isDistinct {
|
||||
b.SelectStmt.Column = []interface{}{fmt.Sprintf("COUNT(DISTINCT %s)", getColumns(column))}
|
||||
b.SelectStmt.IsDistinct = false
|
||||
}
|
||||
|
||||
err = b.LoadOne(&count)
|
||||
// fallback SelectStmt
|
||||
selectStmt.LimitCount = limit
|
||||
selectStmt.OffsetCount = offset
|
||||
selectStmt.Column = column
|
||||
selectStmt.IsDistinct = isDistinct
|
||||
selectStmt.Order = order
|
||||
b.SelectStmt = selectStmt
|
||||
return
|
||||
}
|
||||
|
||||
// InsertQuery
|
||||
// Example: InsertInto().Columns().Record().Exec()
|
||||
|
||||
func (db *Database) InsertInto(table string) *InsertQuery {
|
||||
return &InsertQuery{db.Session.InsertInto(table), db.InsertHook}
|
||||
}
|
||||
|
||||
func (b *InsertQuery) Exec() (sql.Result, error) {
|
||||
result, err := b.InsertBuilder.Exec()
|
||||
if b.Hook != nil && err == nil {
|
||||
defer b.Hook(b)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (b *InsertQuery) Columns(columns ...string) *InsertQuery {
|
||||
b.InsertBuilder.Columns(columns...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *InsertQuery) Record(structValue interface{}) *InsertQuery {
|
||||
b.InsertBuilder.Record(structValue)
|
||||
return b
|
||||
}
|
||||
|
||||
// DeleteQuery
|
||||
// Example: DeleteFrom().Where().Limit().Exec()
|
||||
|
||||
func (db *Database) DeleteFrom(table string) *DeleteQuery {
|
||||
return &DeleteQuery{db.Session.DeleteFrom(table), db.DeleteHook}
|
||||
}
|
||||
|
||||
func (b *DeleteQuery) Where(query interface{}, value ...interface{}) *DeleteQuery {
|
||||
b.DeleteBuilder.Where(query, value...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *DeleteQuery) Limit(n uint64) *DeleteQuery {
|
||||
b.DeleteBuilder.Limit(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *DeleteQuery) Exec() (sql.Result, error) {
|
||||
result, err := b.DeleteBuilder.Exec()
|
||||
if b.Hook != nil && err == nil {
|
||||
defer b.Hook(b)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// UpdateQuery
|
||||
// Example: Update().Set().Where().Exec()
|
||||
|
||||
func (db *Database) Update(table string) *UpdateQuery {
|
||||
return &UpdateQuery{db.Session.Update(table), db.UpdateHook}
|
||||
}
|
||||
|
||||
func (b *UpdateQuery) Exec() (sql.Result, error) {
|
||||
result, err := b.UpdateBuilder.Exec()
|
||||
if b.Hook != nil && err == nil {
|
||||
defer b.Hook(b)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (b *UpdateQuery) Set(column string, value interface{}) *UpdateQuery {
|
||||
b.UpdateBuilder.Set(column, value)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UpdateQuery) SetMap(m map[string]interface{}) *UpdateQuery {
|
||||
b.UpdateBuilder.SetMap(m)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UpdateQuery) Where(query interface{}, value ...interface{}) *UpdateQuery {
|
||||
b.UpdateBuilder.Where(query, value...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UpdateQuery) Limit(n uint64) *UpdateQuery {
|
||||
b.UpdateBuilder.Limit(n)
|
||||
return b
|
||||
}
|
||||
31
pkg/simple/client/mysql/error.go
Normal file
31
pkg/simple/client/mysql/error.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2018 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 mysql
|
||||
|
||||
import (
|
||||
"github.com/gocraft/dbr"
|
||||
)
|
||||
|
||||
// package errors
|
||||
var (
|
||||
ErrNotFound = dbr.ErrNotFound
|
||||
ErrNotSupported = dbr.ErrNotSupported
|
||||
ErrTableNotSpecified = dbr.ErrTableNotSpecified
|
||||
ErrColumnNotSpecified = dbr.ErrColumnNotSpecified
|
||||
ErrInvalidPointer = dbr.ErrInvalidPointer
|
||||
ErrPlaceholderCount = dbr.ErrPlaceholderCount
|
||||
ErrInvalidSliceLength = dbr.ErrInvalidSliceLength
|
||||
ErrCantConvertToTime = dbr.ErrCantConvertToTime
|
||||
ErrInvalidTimestring = dbr.ErrInvalidTimestring
|
||||
)
|
||||
55
pkg/simple/client/mysql/event.go
Normal file
55
pkg/simple/client/mysql/event.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2018 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 mysql
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// EventReceiver is a sentinel EventReceiver; use it if the caller doesn't supply one
|
||||
type EventReceiver struct{}
|
||||
|
||||
// Event receives a simple notification when various events occur
|
||||
func (n *EventReceiver) Event(eventName string) {
|
||||
|
||||
}
|
||||
|
||||
// EventKv receives a notification when various events occur along with
|
||||
// optional key/value data
|
||||
func (n *EventReceiver) EventKv(eventName string, kvs map[string]string) {
|
||||
}
|
||||
|
||||
// EventErr receives a notification of an error if one occurs
|
||||
func (n *EventReceiver) EventErr(eventName string, err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// EventErrKv receives a notification of an error if one occurs along with
|
||||
// optional key/value data
|
||||
func (n *EventReceiver) EventErrKv(eventName string, err error, kvs map[string]string) error {
|
||||
glog.Errorf("%+v", err)
|
||||
glog.Errorf("%s: %+v", eventName, kvs)
|
||||
return err
|
||||
}
|
||||
|
||||
// Timing receives the time an event took to happen
|
||||
func (n *EventReceiver) Timing(eventName string, nanoseconds int64) {
|
||||
|
||||
}
|
||||
|
||||
// TimingKv receives the time an event took to happen along with optional key/value data
|
||||
func (n *EventReceiver) TimingKv(eventName string, nanoseconds int64, kvs map[string]string) {
|
||||
// TODO: Change logger level to debug
|
||||
glog.Infof("%s spend %.2fms: %+v", eventName, float32(nanoseconds)/1000000, kvs)
|
||||
}
|
||||
82
pkg/simple/client/mysql/mysql.go
Normal file
82
pkg/simple/client/mysql/mysql.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2018 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 mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gocraft/dbr"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
type MySQLClient struct {
|
||||
database *Database
|
||||
}
|
||||
|
||||
func NewMySQLClient(options *MySQLOptions, stopCh <-chan struct{}) (*MySQLClient, error) {
|
||||
var m MySQLClient
|
||||
|
||||
conn, err := dbr.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/devops?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", options.Username, options.Password, options.Host), nil)
|
||||
if err != nil {
|
||||
klog.Error("unable to connect to mysql", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.SetMaxIdleConns(options.MaxIdleConnections)
|
||||
conn.SetConnMaxLifetime(options.MaxConnectionLifeTime)
|
||||
conn.SetMaxOpenConns(options.MaxOpenConnections)
|
||||
|
||||
m.database = &Database{
|
||||
Session: conn.NewSession(nil),
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-stopCh
|
||||
if err := conn.Close(); err != nil {
|
||||
klog.Warning("error happened during closing mysql connection", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func NewMySQLClientOrDie(options *MySQLOptions, stopCh <-chan struct{}) *MySQLClient {
|
||||
var m MySQLClient
|
||||
|
||||
conn, err := dbr.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/devops?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", options.Username, options.Password, options.Host), nil)
|
||||
if err != nil {
|
||||
klog.Error("unable to connect to mysql", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn.SetMaxIdleConns(options.MaxIdleConnections)
|
||||
conn.SetConnMaxLifetime(options.MaxConnectionLifeTime)
|
||||
conn.SetMaxOpenConns(options.MaxOpenConnections)
|
||||
|
||||
m.database = &Database{
|
||||
Session: conn.NewSession(nil),
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-stopCh
|
||||
if err := conn.Close(); err != nil {
|
||||
klog.Warning("error happened during closing mysql connection", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
func (m *MySQLClient) Database() *Database {
|
||||
return m.database
|
||||
}
|
||||
43
pkg/simple/client/mysql/options.go
Normal file
43
pkg/simple/client/mysql/options.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
reflectutils "kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MySQLOptions struct {
|
||||
Host string `json:"host,omitempty" yaml:"host,omitempty" description:"MySQL service host address"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty"`
|
||||
Password string `json:"-" yaml:"password,omitempty"`
|
||||
MaxIdleConnections int `json:"maxIdleConnections,omitempty" yaml:"maxIdleConnections,omitempty"`
|
||||
MaxOpenConnections int `json:"maxOpenConnections,omitempty" yaml:"maxOpenConnections,omitempty"`
|
||||
MaxConnectionLifeTime time.Duration `json:"maxConnectionLifeTime,omitempty" yaml:"maxConnectionLifeTime,omitempty"`
|
||||
}
|
||||
|
||||
// NewMySQLOptions create a `zero` value instance
|
||||
func NewMySQLOptions() *MySQLOptions {
|
||||
return &MySQLOptions{}
|
||||
}
|
||||
|
||||
func (m *MySQLOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (m *MySQLOptions) ApplyTo(options *MySQLOptions) {
|
||||
reflectutils.Override(options, m)
|
||||
}
|
||||
|
||||
func (m *MySQLOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
|
||||
fs.StringVar(&m.Host, "mysql-host", m.Host, ""+
|
||||
"MySQL service host address. If left blank, following options will be ignored.")
|
||||
|
||||
fs.StringVar(&m.Username, "mysql-username", m.Username, ""+
|
||||
"Username for access to mysql service.")
|
||||
|
||||
fs.StringVar(&m.Password, "mysql-password", m.Password, ""+
|
||||
"Password for access to mysql, should be used pair with password.")
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,86 +34,9 @@ const (
|
||||
StateSuffix = "-StatefulSet"
|
||||
)
|
||||
|
||||
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 ClusterRole struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type ClusterList 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 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"`
|
||||
}
|
||||
|
||||
type CreateClusterRequest struct {
|
||||
AppId string `json:"app_id" description:"ID of app to run in cluster, e.g. app-AA3A3y3zEgEM"`
|
||||
VersionId string `json:"version_id" description:"app version, e.g. appv-154gXYx5RKRp"`
|
||||
RuntimeId string `json:"runtime_id" description:"ID of runtime, e.g. runtime-wWwXL0LzWqEr"`
|
||||
Conf string `json:"conf" description:"conf a json string, include cpu, memory info of cluster"`
|
||||
}
|
||||
|
||||
type DeleteClusterRequest struct {
|
||||
ClusterId []string `json:"cluster_id" description:"cluster ID"`
|
||||
}
|
||||
|
||||
func GetAppInfo(appId string) (string, string, string, error) {
|
||||
url := fmt.Sprintf("%s/v1/apps?app_id=%s", openpitrixAPIServer, appId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
func (c *OpenPitrixClient) GetAppInfo(appId string) (string, string, string, error) {
|
||||
url := fmt.Sprintf("%s/v1/apps?app_id=%s", c.apiServer, appId)
|
||||
resp, err := c.makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return Unknown, Unknown, Unknown, err
|
||||
@@ -134,14 +56,10 @@ func GetAppInfo(appId string) (string, string, string, error) {
|
||||
return apps.Apps[0].ChartName, apps.Apps[0].RepoId, apps.Apps[0].AppId, nil
|
||||
}
|
||||
|
||||
func GetCluster(clusterId string) (*Cluster, error) {
|
||||
if strings.HasSuffix(openpitrixAPIServer, "/") {
|
||||
openpitrixAPIServer = strings.TrimSuffix(openpitrixAPIServer, "/")
|
||||
}
|
||||
func (c *OpenPitrixClient) GetCluster(clusterId string) (*Cluster, error) {
|
||||
url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", c.apiServer, clusterId)
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", openpitrixAPIServer, clusterId)
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
resp, err := c.makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
@@ -162,14 +80,11 @@ func GetCluster(clusterId string) (*Cluster, error) {
|
||||
return &clusterList.Clusters[0], nil
|
||||
}
|
||||
|
||||
func ListClusters(runtimeId, searchWord, status string, limit, offset int) (*ClusterList, error) {
|
||||
if strings.HasSuffix(openpitrixAPIServer, "/") {
|
||||
openpitrixAPIServer = strings.TrimSuffix(openpitrixAPIServer, "/")
|
||||
}
|
||||
func (c *OpenPitrixClient) ListClusters(runtimeId, searchWord, status string, limit, offset int) (*ClusterList, error) {
|
||||
|
||||
defaultStatus := "status=active&status=stopped&status=pending&status=ceased"
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", openpitrixAPIServer, strconv.Itoa(limit), strconv.Itoa(offset))
|
||||
url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", c.apiServer, strconv.Itoa(limit), strconv.Itoa(offset))
|
||||
|
||||
if searchWord != "" {
|
||||
url = fmt.Sprintf("%s&search_word=%s", url, searchWord)
|
||||
@@ -185,7 +100,7 @@ func ListClusters(runtimeId, searchWord, status string, limit, offset int) (*Clu
|
||||
url = fmt.Sprintf("%s&runtime_id=%s", url, runtimeId)
|
||||
}
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
resp, err := c.makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Errorf("request %s failed, reason: %s", url, err)
|
||||
return nil, err
|
||||
@@ -201,9 +116,9 @@ func ListClusters(runtimeId, searchWord, status string, limit, offset int) (*Clu
|
||||
return &clusterList, nil
|
||||
}
|
||||
|
||||
func GetRepo(repoId string) (string, error) {
|
||||
url := fmt.Sprintf("%s/v1/repos?repo_id=%s", openpitrixAPIServer, repoId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
func (c *OpenPitrixClient) GetRepo(repoId string) (string, error) {
|
||||
url := fmt.Sprintf("%s/v1/repos?repo_id=%s", c.apiServer, repoId)
|
||||
resp, err := c.makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return Unknown, err
|
||||
@@ -223,9 +138,9 @@ func GetRepo(repoId string) (string, error) {
|
||||
return repos.Repos[0].Name, nil
|
||||
}
|
||||
|
||||
func GetVersion(versionId string) (string, error) {
|
||||
versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", openpitrixAPIServer, versionId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
func (c *OpenPitrixClient) GetVersion(versionId string) (string, error) {
|
||||
versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", c.apiServer, versionId)
|
||||
resp, err := c.makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return Unknown, err
|
||||
@@ -244,10 +159,10 @@ func GetVersion(versionId string) (string, error) {
|
||||
return versions.Versions[0].Name, nil
|
||||
}
|
||||
|
||||
func GetRuntime(runtimeId string) (string, error) {
|
||||
func (c *OpenPitrixClient) GetRuntime(runtimeId string) (string, error) {
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", openpitrixAPIServer, runtimeId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", c.apiServer, runtimeId)
|
||||
resp, err := c.makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return Unknown, err
|
||||
@@ -267,9 +182,9 @@ func GetRuntime(runtimeId string) (string, error) {
|
||||
return runtimes.Runtimes[0].Zone, nil
|
||||
}
|
||||
|
||||
func CreateCluster(request CreateClusterRequest) error {
|
||||
func (c *OpenPitrixClient) CreateCluster(request CreateClusterRequest) error {
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/v1/clusters/create", openpitrixAPIServer)
|
||||
versionUrl := fmt.Sprintf("%s/v1/clusters/create", c.apiServer)
|
||||
|
||||
data, err := json.Marshal(request)
|
||||
|
||||
@@ -278,7 +193,7 @@ func CreateCluster(request CreateClusterRequest) error {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err = makeHttpRequest("POST", versionUrl, string(data))
|
||||
data, err = c.makeHttpRequest("POST", versionUrl, string(data))
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
@@ -288,9 +203,9 @@ func CreateCluster(request CreateClusterRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteCluster(request DeleteClusterRequest) error {
|
||||
func (c *OpenPitrixClient) DeleteCluster(request DeleteClusterRequest) error {
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/v1/clusters/delete", openpitrixAPIServer)
|
||||
versionUrl := fmt.Sprintf("%s/v1/clusters/delete", c.apiServer)
|
||||
|
||||
data, err := json.Marshal(request)
|
||||
|
||||
@@ -299,7 +214,7 @@ func DeleteCluster(request DeleteClusterRequest) error {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err = makeHttpRequest("POST", versionUrl, string(data))
|
||||
data, err = c.makeHttpRequest("POST", versionUrl, string(data))
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
@@ -309,7 +224,7 @@ func DeleteCluster(request DeleteClusterRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
func (c *OpenPitrixClient) makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
var req *http.Request
|
||||
|
||||
var err error
|
||||
@@ -319,7 +234,7 @@ func makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
req, err = http.NewRequest(method, url, strings.NewReader(data))
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", openpitrixProxyToken)
|
||||
req.Header.Add("Authorization", c.token)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
@@ -329,7 +244,7 @@ func makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Request to %s failed, method: %s,token: %s, reason: %s ", url, method, openpitrixProxyToken, err)
|
||||
err := fmt.Errorf("Request to %s failed, method: %s,token: %s, reason: %s ", url, method, c.apiServer, err)
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -20,105 +20,64 @@ package openpitrix
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"io/ioutil"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
openpitrixAPIServer string
|
||||
openpitrixProxyToken string
|
||||
once sync.Once
|
||||
c client
|
||||
)
|
||||
|
||||
type RunTime struct {
|
||||
RuntimeId string `json:"runtime_id"`
|
||||
RuntimeUrl string `json:"runtime_url"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Zone string `json:"zone"`
|
||||
RuntimeCredential string `json:"runtime_credential"`
|
||||
func NewOpenPitrixClient(options *OpenPitrixOptions) (*OpenPitrixClient, error) {
|
||||
return &OpenPitrixClient{
|
||||
client: &http.Client{
|
||||
Timeout: time.Duration(3) * time.Second,
|
||||
},
|
||||
apiServer: options.APIServer,
|
||||
token: options.Token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
CreateRuntime(runtime *RunTime) error
|
||||
DeleteRuntime(runtimeId string) error
|
||||
}
|
||||
type cluster struct {
|
||||
Status string `json:"status"`
|
||||
ClusterId string `json:"cluster_id"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
status int
|
||||
message string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("status: %d,message: %s", e.status, e.message)
|
||||
}
|
||||
|
||||
type client struct {
|
||||
client http.Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&openpitrixAPIServer, "openpitrix-api-server", "http://openpitrix-api-gateway.openpitrix-system.svc:9100", "openpitrix api server")
|
||||
flag.StringVar(&openpitrixProxyToken, "openpitrix-proxy-token", "", "openpitrix proxy token")
|
||||
}
|
||||
|
||||
func Client() Interface {
|
||||
once.Do(func() {
|
||||
c = client{client: http.Client{}}
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
func (c client) CreateRuntime(runtime *RunTime) error {
|
||||
func (c *OpenPitrixClient) CreateRuntime(runtime *RunTime) error {
|
||||
|
||||
data, err := json.Marshal(runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/runtimes", openpitrixAPIServer), bytes.NewReader(data))
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/runtimes", c.apiServer), bytes.NewReader(data))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", openpitrixProxyToken)
|
||||
req.Header.Add("Authorization", c.token)
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode > http.StatusOK {
|
||||
err = Error{resp.StatusCode, string(data)}
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c client) deleteClusters(clusters []cluster) error {
|
||||
func (c *OpenPitrixClient) deleteClusters(clusters []cluster) error {
|
||||
clusterId := make([]string, 0)
|
||||
|
||||
for _, cluster := range clusters {
|
||||
@@ -137,16 +96,16 @@ func (c client) deleteClusters(clusters []cluster) error {
|
||||
ClusterId: clusterId,
|
||||
}
|
||||
data, _ := json.Marshal(deleteRequest)
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/clusters/delete", openpitrixAPIServer), bytes.NewReader(data))
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/clusters/delete", c.apiServer), bytes.NewReader(data))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Authorization", openpitrixProxyToken)
|
||||
req.Header.Add("Authorization", c.token)
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -154,44 +113,44 @@ func (c client) deleteClusters(clusters []cluster) error {
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode > http.StatusOK {
|
||||
err = Error{resp.StatusCode, string(data)}
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c client) listClusters(runtimeId string) ([]cluster, error) {
|
||||
func (c *OpenPitrixClient) listClusters(runtimeId string) ([]cluster, error) {
|
||||
limit := 200
|
||||
offset := 0
|
||||
clusters := make([]cluster, 0)
|
||||
for {
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/clusters?runtime_id=%s&limit=%d&offset=%d", openpitrixAPIServer, runtimeId, limit, offset), nil)
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/clusters?runtime_id=%s&limit=%d&offset=%d", c.apiServer, runtimeId, limit, offset), nil)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", openpitrixProxyToken)
|
||||
req.Header.Add("Authorization", c.token)
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -199,7 +158,7 @@ func (c client) listClusters(runtimeId string) ([]cluster, error) {
|
||||
|
||||
if resp.StatusCode > http.StatusOK {
|
||||
err = Error{resp.StatusCode, string(data)}
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
listClusterResponse := struct {
|
||||
@@ -209,7 +168,7 @@ func (c client) listClusters(runtimeId string) ([]cluster, error) {
|
||||
err = json.Unmarshal(data, &listClusterResponse)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -225,18 +184,18 @@ func (c client) listClusters(runtimeId string) ([]cluster, error) {
|
||||
return clusters, nil
|
||||
}
|
||||
|
||||
func (c client) DeleteRuntime(runtimeId string) error {
|
||||
func (c *OpenPitrixClient) DeleteRuntime(runtimeId string) error {
|
||||
clusters, err := c.listClusters(runtimeId)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.deleteClusters(clusters)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
43
pkg/simple/client/openpitrix/options.go
Normal file
43
pkg/simple/client/openpitrix/options.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package openpitrix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
)
|
||||
|
||||
type OpenPitrixOptions struct {
|
||||
APIServer string `json:"apiServer,omitempty" yaml:"apiServer,omitempty"`
|
||||
Token string `json:"token,omitempty" yaml:"token,omitempty"`
|
||||
}
|
||||
|
||||
func NewOpenPitrixOptions() *OpenPitrixOptions {
|
||||
return &OpenPitrixOptions{
|
||||
APIServer: "",
|
||||
Token: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OpenPitrixOptions) ApplyTo(options *OpenPitrixOptions) {
|
||||
reflectutils.Override(s, options)
|
||||
}
|
||||
|
||||
func (s *OpenPitrixOptions) Validate() []error {
|
||||
errs := []error{}
|
||||
|
||||
if s.APIServer != "" {
|
||||
if s.Token == "" {
|
||||
errs = append(errs, fmt.Errorf("OpenPitrix access token cannot be empty"))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (s *OpenPitrixOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.APIServer, "openpitrix-apiserver", s.APIServer, ""+
|
||||
"OpenPitrix api gateway endpoint, if left blank, following options will be ignored.")
|
||||
|
||||
fs.StringVar(&s.Token, "openpitrix-token", s.Token, ""+
|
||||
"OpenPitrix api access token.")
|
||||
}
|
||||
117
pkg/simple/client/openpitrix/types.go
Normal file
117
pkg/simple/client/openpitrix/types.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package openpitrix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 ClusterRole struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type ClusterList 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 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"`
|
||||
}
|
||||
|
||||
type CreateClusterRequest struct {
|
||||
AppId string `json:"app_id" description:"ID of app to run in cluster, e.g. app-AA3A3y3zEgEM"`
|
||||
VersionId string `json:"version_id" description:"app version, e.g. appv-154gXYx5RKRp"`
|
||||
RuntimeId string `json:"runtime_id" description:"ID of runtime, e.g. runtime-wWwXL0LzWqEr"`
|
||||
Conf string `json:"conf" description:"conf a json string, include cpu, memory info of cluster"`
|
||||
}
|
||||
|
||||
type DeleteClusterRequest struct {
|
||||
ClusterId []string `json:"cluster_id" description:"cluster ID"`
|
||||
}
|
||||
|
||||
type RunTime struct {
|
||||
RuntimeId string `json:"runtime_id"`
|
||||
RuntimeUrl string `json:"runtime_url"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Zone string `json:"zone"`
|
||||
RuntimeCredential string `json:"runtime_credential"`
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
CreateRuntime(runtime *RunTime) error
|
||||
DeleteRuntime(runtimeId string) error
|
||||
}
|
||||
type cluster struct {
|
||||
Status string `json:"status"`
|
||||
ClusterId string `json:"cluster_id"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
status int
|
||||
message string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("status: %d,message: %s", e.status, e.message)
|
||||
}
|
||||
|
||||
type OpenPitrixClient struct {
|
||||
client *http.Client
|
||||
apiServer string
|
||||
token string
|
||||
}
|
||||
37
pkg/simple/client/prometheus/options.go
Normal file
37
pkg/simple/client/prometheus/options.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
)
|
||||
|
||||
type PrometheusOptions struct {
|
||||
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
func NewPrometheusOptions() *PrometheusOptions {
|
||||
return &PrometheusOptions{
|
||||
Endpoint: "",
|
||||
SecondaryEndpoint: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PrometheusOptions) Validate() []error {
|
||||
errs := []error{}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (s *PrometheusOptions) ApplyTo(options *PrometheusOptions) {
|
||||
reflectutils.Override(s, options)
|
||||
}
|
||||
|
||||
func (s *PrometheusOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.Endpoint, "prometheus-endpoint", s.Endpoint, ""+
|
||||
"Prometheus service endpoint which stores KubeSphere monitoring data, if left "+
|
||||
"blank, will use builtin metrics-server as data source.")
|
||||
|
||||
fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", s.SecondaryEndpoint, ""+
|
||||
"Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.")
|
||||
}
|
||||
67
pkg/simple/client/prometheus/prometheus.go
Normal file
67
pkg/simple/client/prometheus/prometheus.go
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
|
||||
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 prometheus
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PrometheusClient struct {
|
||||
client *http.Client
|
||||
endpoint string
|
||||
secondaryEndpoint string
|
||||
}
|
||||
|
||||
func NewPrometheusClient(options *PrometheusOptions) (*PrometheusClient, error) {
|
||||
return &PrometheusClient{
|
||||
client: &http.Client{
|
||||
Timeout: time.Duration(3) * time.Second,
|
||||
},
|
||||
endpoint: options.Endpoint,
|
||||
secondaryEndpoint: options.SecondaryEndpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *PrometheusClient) SendMonitoringRequest(queryType string, params string) string {
|
||||
return c.sendMonitoringRequest(c.endpoint, queryType, params)
|
||||
}
|
||||
|
||||
func (c *PrometheusClient) SendSecondaryMonitoringRequest(queryType string, params string) string {
|
||||
return c.sendMonitoringRequest(c.secondaryEndpoint, queryType, params)
|
||||
}
|
||||
|
||||
func (c *PrometheusClient) sendMonitoringRequest(endpoint string, queryType string, params string) string {
|
||||
epurl := endpoint + queryType + params
|
||||
response, err := c.client.Get(epurl)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
return string(contents)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
|
||||
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 prometheus
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultQueryStep = "10m"
|
||||
DefaultQueryTimeout = "10s"
|
||||
RangeQueryType = "query_range?"
|
||||
DefaultQueryType = "query?"
|
||||
)
|
||||
|
||||
// Kubesphere sets up two Prometheus servers to balance monitoring workloads
|
||||
var (
|
||||
PrometheusEndpoint string // For monitoring node, namespace, pod ... level resources
|
||||
SecondaryPrometheusEndpoint string // For monitoring components including etcd, apiserver, coredns, etc.
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&PrometheusEndpoint, "prometheus-endpoint", "http://prometheus-k8s.kubesphere-monitoring-system.svc:9090/api/v1/", "For physical and k8s resource monitoring, including node, namespace, pod, etc.")
|
||||
flag.StringVar(&SecondaryPrometheusEndpoint, "secondary-prometheus-endpoint", "http://prometheus-k8s-system.kubesphere-monitoring-system.svc:9090/api/v1/", "For k8s component monitoring, including etcd, apiserver, coredns, etc.")
|
||||
}
|
||||
|
||||
type MonitoringRequestParams struct {
|
||||
Params url.Values
|
||||
QueryType string
|
||||
SortMetricName string
|
||||
SortType string
|
||||
PageNum string
|
||||
LimitNum string
|
||||
Tp string
|
||||
MetricsFilter string
|
||||
ResourcesFilter string
|
||||
MetricsName string
|
||||
WorkloadName string
|
||||
NodeId string
|
||||
WsName string
|
||||
NsName string
|
||||
PodName string
|
||||
PVCName string
|
||||
StorageClassName string
|
||||
ContainerName string
|
||||
WorkloadKind string
|
||||
ComponentName string
|
||||
}
|
||||
|
||||
var client = &http.Client{}
|
||||
|
||||
func SendMonitoringRequest(prometheusEndpoint string, queryType string, params string) string {
|
||||
epurl := prometheusEndpoint + queryType + params
|
||||
response, err := client.Get(epurl)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
return string(contents)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ParseMonitoringRequestParams(request *restful.Request) *MonitoringRequestParams {
|
||||
instantTime := strings.Trim(request.QueryParameter("time"), " ")
|
||||
start := strings.Trim(request.QueryParameter("start"), " ")
|
||||
end := strings.Trim(request.QueryParameter("end"), " ")
|
||||
step := strings.Trim(request.QueryParameter("step"), " ")
|
||||
timeout := strings.Trim(request.QueryParameter("timeout"), " ")
|
||||
|
||||
sortMetricName := strings.Trim(request.QueryParameter("sort_metric"), " ")
|
||||
sortType := strings.Trim(request.QueryParameter("sort_type"), " ")
|
||||
pageNum := strings.Trim(request.QueryParameter("page"), " ")
|
||||
limitNum := strings.Trim(request.QueryParameter("limit"), " ")
|
||||
tp := strings.Trim(request.QueryParameter("type"), " ")
|
||||
|
||||
metricsFilter := strings.Trim(request.QueryParameter("metrics_filter"), " ")
|
||||
resourcesFilter := strings.Trim(request.QueryParameter("resources_filter"), " ")
|
||||
|
||||
metricsName := strings.Trim(request.QueryParameter("metrics_name"), " ")
|
||||
workloadName := strings.Trim(request.PathParameter("workload"), " ")
|
||||
|
||||
nodeId := strings.Trim(request.PathParameter("node"), " ")
|
||||
wsName := strings.Trim(request.PathParameter("workspace"), " ")
|
||||
nsName := strings.Trim(request.PathParameter("namespace"), " ")
|
||||
podName := strings.Trim(request.PathParameter("pod"), " ")
|
||||
pvcName := strings.Trim(request.PathParameter("pvc"), " ")
|
||||
storageClassName := strings.Trim(request.PathParameter("storageclass"), " ")
|
||||
containerName := strings.Trim(request.PathParameter("container"), " ")
|
||||
workloadKind := strings.Trim(request.PathParameter("kind"), " ")
|
||||
componentName := strings.Trim(request.PathParameter("component"), " ")
|
||||
|
||||
var requestParams = MonitoringRequestParams{
|
||||
SortMetricName: sortMetricName,
|
||||
SortType: sortType,
|
||||
PageNum: pageNum,
|
||||
LimitNum: limitNum,
|
||||
Tp: tp,
|
||||
MetricsFilter: metricsFilter,
|
||||
ResourcesFilter: resourcesFilter,
|
||||
MetricsName: metricsName,
|
||||
WorkloadName: workloadName,
|
||||
NodeId: nodeId,
|
||||
WsName: wsName,
|
||||
NsName: nsName,
|
||||
PodName: podName,
|
||||
PVCName: pvcName,
|
||||
StorageClassName: storageClassName,
|
||||
ContainerName: containerName,
|
||||
WorkloadKind: workloadKind,
|
||||
ComponentName: componentName,
|
||||
}
|
||||
|
||||
if timeout == "" {
|
||||
timeout = DefaultQueryTimeout
|
||||
}
|
||||
if step == "" {
|
||||
step = DefaultQueryStep
|
||||
}
|
||||
// Whether query or query_range request
|
||||
u := url.Values{}
|
||||
|
||||
if start != "" && end != "" {
|
||||
|
||||
u.Set("start", convertTimeGranularity(start))
|
||||
u.Set("end", convertTimeGranularity(end))
|
||||
u.Set("step", step)
|
||||
u.Set("timeout", timeout)
|
||||
|
||||
// range query start time must be greater than the namespace creation time
|
||||
if nsName != "" {
|
||||
nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
|
||||
ns, err := nsLister.Get(nsName)
|
||||
if err == nil {
|
||||
queryStartTime := u.Get("start")
|
||||
nsCreationTime := strconv.FormatInt(ns.CreationTimestamp.Unix(), 10)
|
||||
if nsCreationTime > queryStartTime {
|
||||
u.Set("start", nsCreationTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestParams.QueryType = RangeQueryType
|
||||
requestParams.Params = u
|
||||
|
||||
return &requestParams
|
||||
}
|
||||
if instantTime != "" {
|
||||
u.Set("time", instantTime)
|
||||
u.Set("timeout", timeout)
|
||||
requestParams.QueryType = DefaultQueryType
|
||||
requestParams.Params = u
|
||||
return &requestParams
|
||||
} else {
|
||||
u.Set("timeout", timeout)
|
||||
requestParams.QueryType = DefaultQueryType
|
||||
requestParams.Params = u
|
||||
return &requestParams
|
||||
}
|
||||
}
|
||||
|
||||
func convertTimeGranularity(ts string) string {
|
||||
timeFloat, err := strconv.ParseFloat(ts, 64)
|
||||
if err != nil {
|
||||
glog.Errorf("convert second timestamp %s to minute timestamp failed", ts)
|
||||
return strconv.FormatInt(int64(time.Now().Unix()), 10)
|
||||
}
|
||||
timeInt := int64(timeFloat)
|
||||
// convert second timestamp to minute timestamp
|
||||
secondTime := time.Unix(timeInt, 0).Truncate(time.Minute).Unix()
|
||||
return strconv.FormatInt(secondTime, 10)
|
||||
}
|
||||
57
pkg/simple/client/redis/options.go
Normal file
57
pkg/simple/client/redis/options.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/utils/net"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
)
|
||||
|
||||
type RedisOptions struct {
|
||||
Host string
|
||||
Port int
|
||||
Password string
|
||||
DB int
|
||||
}
|
||||
|
||||
// NewRedisOptions returns options points to nowhere,
|
||||
// because redis is not required for some components
|
||||
func NewRedisOptions() *RedisOptions {
|
||||
return &RedisOptions{
|
||||
Host: "",
|
||||
Port: 6379,
|
||||
Password: "",
|
||||
DB: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RedisOptions) Validate() []error {
|
||||
errors := make([]error, 0)
|
||||
|
||||
if r.Host != "" {
|
||||
if !net.IsValidPort(r.Port) {
|
||||
errors = append(errors, fmt.Errorf("--redis-port is out of range"))
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (r *RedisOptions) ApplyTo(options *RedisOptions) {
|
||||
reflectutils.Override(options, r)
|
||||
}
|
||||
|
||||
func (r *RedisOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&r.Host, "--redis-host", r.Host, ""+
|
||||
"Redis service host address. If left blank, means redis is unnecessary, "+
|
||||
"redis will be disabled")
|
||||
|
||||
fs.IntVar(&r.Port, "--redis-port", r.Port, ""+
|
||||
"Redis service port number.")
|
||||
|
||||
fs.StringVar(&r.Password, "--redis-password", r.Password, ""+
|
||||
"Redis service password if necessary, default to empty")
|
||||
|
||||
fs.IntVar(&r.DB, "--redis-db", r.DB, ""+
|
||||
"Redis service database index, default to 0.")
|
||||
}
|
||||
@@ -18,48 +18,49 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"fmt"
|
||||
"github.com/go-redis/redis"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
var (
|
||||
redisHost string
|
||||
redisPassword string
|
||||
redisDB int
|
||||
redisClientOnce sync.Once
|
||||
redisClient *redis.Client
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&redisHost, "redis-server", "localhost:6379", "redis server host")
|
||||
flag.StringVar(&redisPassword, "redis-password", "", "redis password")
|
||||
flag.IntVar(&redisDB, "redis-db", 0, "redis db")
|
||||
type RedisClient struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
func Client() *redis.Client {
|
||||
func NewRedisClientOrDie(options *RedisOptions, stopCh <-chan struct{}) *RedisClient {
|
||||
client, err := NewRedisClient(options, stopCh)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
redisClientOnce.Do(func() {
|
||||
redisClient = redis.NewClient(&redis.Options{
|
||||
Addr: redisHost,
|
||||
Password: redisPassword,
|
||||
DB: redisDB,
|
||||
})
|
||||
if err := redisClient.Ping().Err(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
c := make(chan os.Signal, 0)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
redisClient.Close()
|
||||
}()
|
||||
return client
|
||||
}
|
||||
|
||||
func NewRedisClient(option *RedisOptions, stopCh <-chan struct{}) (*RedisClient, error) {
|
||||
var r RedisClient
|
||||
|
||||
r.client = redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", option.Host, option.Port),
|
||||
Password: option.Password,
|
||||
DB: option.DB,
|
||||
})
|
||||
|
||||
return redisClient
|
||||
if err := r.client.Ping().Err(); err != nil {
|
||||
klog.Error("unable to reach redis host", err)
|
||||
r.client.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-stopCh
|
||||
if err := r.client.Close(); err != nil {
|
||||
klog.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (r *RedisClient) Redis() *redis.Client {
|
||||
return r.client
|
||||
}
|
||||
|
||||
61
pkg/simple/client/s2is3/options.go
Normal file
61
pkg/simple/client/s2is3/options.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package s2is3
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
)
|
||||
|
||||
type S3Options struct {
|
||||
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
Region string `json:"region,omitempty" yaml:"region,omitempty"`
|
||||
DisableSSL bool `json:"disableSSL,omitempty" yaml:"disableSSL,omitempty"`
|
||||
ForcePathStyle bool `json:"forcePathStyle,omitempty" yaml:"forePathStyle,omitempty"`
|
||||
AccessKeyID string `json:"accessKeyID,omitempty" yaml:"accessKeyID,omitempty"`
|
||||
SecretAccessKey string `json:"secretAccessKey,omitempty" yaml:"secretAccessKey,omitempty"`
|
||||
SessionToken string `json:"sessionToken,omitempty" yaml:"sessionToken,omitempty"`
|
||||
Bucket string `json:"bucket,omitempty" yaml:"bucket,omitempty"`
|
||||
}
|
||||
|
||||
func NewS3Options() *S3Options {
|
||||
return &S3Options{
|
||||
Endpoint: "",
|
||||
Region: "",
|
||||
DisableSSL: true,
|
||||
ForcePathStyle: true,
|
||||
AccessKeyID: "",
|
||||
SecretAccessKey: "",
|
||||
SessionToken: "",
|
||||
Bucket: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S3Options) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (s *S3Options) ApplyTo(options *S3Options) {
|
||||
reflectutils.Override(options, s)
|
||||
}
|
||||
|
||||
func (s *S3Options) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.Endpoint, "s3-endpoint", s.Endpoint, ""+
|
||||
"Endpoint to access to s3 object storage service, if left blank, the following options "+
|
||||
"will be ignored.")
|
||||
|
||||
fs.StringVar(&s.Region, "s3-region", s.Region, ""+
|
||||
"Region of s3 that will access to, like us-east-1.")
|
||||
|
||||
fs.StringVar(&s.AccessKeyID, "s3-access-key-id", "AKIAIOSFODNN7EXAMPLE", "access key of s2i s3")
|
||||
|
||||
fs.StringVar(&s.SecretAccessKey, "s3-secret-access-key", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "secret access key of s2i s3")
|
||||
|
||||
fs.StringVar(&s.SessionToken, "s3-session-token", s.SessionToken, "session token of s2i s3")
|
||||
|
||||
fs.StringVar(&s.Bucket, "s3-bucket", "s2i-binaries", "bucket name of s2i s3")
|
||||
|
||||
fs.BoolVar(&s.DisableSSL, "s3-disable-SSL", s.DisableSSL, "disable ssl")
|
||||
|
||||
fs.BoolVar(&s.ForcePathStyle, "s3-force-path-style", true, "force path style")
|
||||
}
|
||||
@@ -1,83 +1,79 @@
|
||||
package s2is3
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"k8s.io/klog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
s3Region string
|
||||
s3Endpoint string
|
||||
s3DisableSSL bool
|
||||
s3ForcePathStyle bool
|
||||
s3AccessKeyID string
|
||||
s3SecretAccessKey string
|
||||
s3SessionToken string
|
||||
s3Bucket string
|
||||
)
|
||||
var (
|
||||
s2iS3 *s3.S3
|
||||
s2iS3Session *session.Session
|
||||
sessionInitMutex sync.Mutex
|
||||
clientInitMutex sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&s3Region, "s2i-s3-region", "us-east-1", "region of s2i s3")
|
||||
flag.StringVar(&s3Endpoint, "s2i-s3-endpoint", "http://ks-minio.kubesphere-system.svc", "endpoint of s2i s3")
|
||||
flag.StringVar(&s3AccessKeyID, "s2i-s3-access-key-id", "AKIAIOSFODNN7EXAMPLE", "access key of s2i s3")
|
||||
flag.StringVar(&s3SecretAccessKey, "s2i-s3-secret-access-key", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "secret access key of s2i s3")
|
||||
flag.StringVar(&s3SessionToken, "s2i-s3-session-token", "", "session token of s2i s3")
|
||||
flag.StringVar(&s3Bucket, "s2i-s3-bucket", "s2i-binaries", "bucket name of s2i s3")
|
||||
flag.BoolVar(&s3DisableSSL, "s2i-s3-disable-SSL", true, "disable ssl")
|
||||
flag.BoolVar(&s3ForcePathStyle, "s2i-s3-force-path-style", true, "force path style")
|
||||
type S3Client struct {
|
||||
s3Client *s3.S3
|
||||
s3Session *session.Session
|
||||
bucket string
|
||||
}
|
||||
|
||||
func Client() *s3.S3 {
|
||||
if s2iS3 != nil {
|
||||
return s2iS3
|
||||
func NewS3Client(options *S3Options) (*S3Client, error) {
|
||||
cred := credentials.NewStaticCredentials(options.AccessKeyID, options.SecretAccessKey, options.SessionToken)
|
||||
|
||||
config := aws.Config{
|
||||
Region: aws.String(options.Region),
|
||||
Endpoint: aws.String(options.Endpoint),
|
||||
DisableSSL: aws.Bool(options.DisableSSL),
|
||||
S3ForcePathStyle: aws.Bool(options.ForcePathStyle),
|
||||
Credentials: cred,
|
||||
}
|
||||
clientInitMutex.Lock()
|
||||
defer clientInitMutex.Unlock()
|
||||
if s2iS3Session == nil {
|
||||
if sess := Session(); sess != nil {
|
||||
klog.Error("failed to connect to s2i s3")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
s2iS3 = s3.New(s2iS3Session)
|
||||
return s2iS3
|
||||
}
|
||||
func Session() *session.Session {
|
||||
if s2iS3Session != nil {
|
||||
return s2iS3Session
|
||||
}
|
||||
sessionInitMutex.Lock()
|
||||
defer sessionInitMutex.Unlock()
|
||||
creds := credentials.NewStaticCredentials(
|
||||
s3AccessKeyID, s3SecretAccessKey, s3SessionToken,
|
||||
)
|
||||
config := &aws.Config{
|
||||
Region: aws.String(s3Region),
|
||||
Endpoint: aws.String(s3Endpoint),
|
||||
DisableSSL: aws.Bool(s3DisableSSL),
|
||||
S3ForcePathStyle: aws.Bool(s3ForcePathStyle),
|
||||
Credentials: creds,
|
||||
}
|
||||
sess, err := session.NewSession(config)
|
||||
|
||||
s, err := session.NewSession(&config)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to connect to s2i s3: %+v", err)
|
||||
return nil
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
s2iS3Session = sess
|
||||
return s2iS3Session
|
||||
|
||||
var c S3Client
|
||||
|
||||
c.s3Client = s3.New(s)
|
||||
c.s3Session = s
|
||||
c.bucket = options.Bucket
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func Bucket() *string {
|
||||
return aws.String(s3Bucket)
|
||||
// NewS3ClientOrDie creates S3Client and panics if there is an error
|
||||
func NewS3ClientOrDie(options *S3Options) *S3Client {
|
||||
cred := credentials.NewStaticCredentials(options.AccessKeyID, options.SecretAccessKey, options.SessionToken)
|
||||
|
||||
config := aws.Config{
|
||||
Region: aws.String(options.Region),
|
||||
Endpoint: aws.String(options.Endpoint),
|
||||
DisableSSL: aws.Bool(options.DisableSSL),
|
||||
S3ForcePathStyle: aws.Bool(options.ForcePathStyle),
|
||||
Credentials: cred,
|
||||
}
|
||||
|
||||
s, err := session.NewSession(&config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
client := s3.New(s)
|
||||
|
||||
return &S3Client{
|
||||
s3Client: client,
|
||||
s3Session: s,
|
||||
bucket: options.Bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S3Client) Client() *s3.S3 {
|
||||
|
||||
return s.s3Client
|
||||
}
|
||||
func (s *S3Client) Session() *session.Session {
|
||||
return s.s3Session
|
||||
}
|
||||
|
||||
func (s *S3Client) Bucket() *string {
|
||||
return aws.String(s.bucket)
|
||||
}
|
||||
|
||||
59
pkg/simple/client/servicemesh/options.go
Normal file
59
pkg/simple/client/servicemesh/options.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package servicemesh
|
||||
|
||||
import "github.com/spf13/pflag"
|
||||
|
||||
type ServiceMeshOptions struct {
|
||||
|
||||
// istio pilot discovery service url
|
||||
IstioPilotHost string `json:"istioPilotHost,omitempty" yaml:"istioPilotHost,omitempty"`
|
||||
|
||||
// jaeger query service url
|
||||
JaegerQueryHost string `json:"jaegerQueryHost,omitempty" yaml:"jaegerQueryHost,omitempty"`
|
||||
|
||||
// prometheus service url for servicemesh metrics
|
||||
ServicemeshPrometheusHost string `json:"servicemeshPrometheusHost,omitempty" yaml:"servicemeshPrometheusHost,omitempty"`
|
||||
}
|
||||
|
||||
// NewServiceMeshOptions returns a `zero` instance
|
||||
func NewServiceMeshOptions() *ServiceMeshOptions {
|
||||
return &ServiceMeshOptions{
|
||||
IstioPilotHost: "",
|
||||
JaegerQueryHost: "",
|
||||
ServicemeshPrometheusHost: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceMeshOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (s *ServiceMeshOptions) ApplyTo(options *ServiceMeshOptions) {
|
||||
if options == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.ServicemeshPrometheusHost != "" {
|
||||
options.ServicemeshPrometheusHost = s.ServicemeshPrometheusHost
|
||||
}
|
||||
|
||||
if s.JaegerQueryHost != "" {
|
||||
options.JaegerQueryHost = s.JaegerQueryHost
|
||||
}
|
||||
|
||||
if s.IstioPilotHost != "" {
|
||||
options.IstioPilotHost = s.IstioPilotHost
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceMeshOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.IstioPilotHost, "istio-pilot-host", s.IstioPilotHost, ""+
|
||||
"istio pilot discovery service url")
|
||||
|
||||
fs.StringVar(&s.JaegerQueryHost, "jaeger-query-host", s.JaegerQueryHost, ""+
|
||||
"jaeger query service url")
|
||||
|
||||
fs.StringVar(&s.ServicemeshPrometheusHost, "servicemesh-prometheus-host", s.ServicemeshPrometheusHost, ""+
|
||||
"prometheus service for servicemesh")
|
||||
}
|
||||
49
pkg/simple/client/sonarqube/options.go
Normal file
49
pkg/simple/client/sonarqube/options.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package sonarqube
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type SonarQubeOptions struct {
|
||||
Host string `json:",omitempty" yaml:",omitempty" description:"SonarQube service host address"`
|
||||
Token string `json:",omitempty" yaml:",omitempty" description:"SonarQube service token"`
|
||||
}
|
||||
|
||||
func NewSonarQubeOptions() *SonarQubeOptions {
|
||||
return &SonarQubeOptions{
|
||||
Host: "",
|
||||
Token: "",
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultSonarQubeOptions() *SonarQubeOptions {
|
||||
return NewSonarQubeOptions()
|
||||
}
|
||||
|
||||
func (s *SonarQubeOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (s *SonarQubeOptions) ApplyTo(options *SonarQubeOptions) {
|
||||
if options == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.Host != "" {
|
||||
options.Host = s.Host
|
||||
}
|
||||
|
||||
if s.Token != "" {
|
||||
options.Token = s.Token
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SonarQubeOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.Host, "sonarqube-host", s.Host, ""+
|
||||
"Sonarqube service address if enabled.")
|
||||
|
||||
fs.StringVar(&s.Token, "sonarqube-token", s.Token, ""+
|
||||
"Sonarqube service access token.")
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package sonarqube
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/golang/glog"
|
||||
"github.com/kubesphere/sonargo/sonar"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
sonarAddress string
|
||||
sonarToken string
|
||||
sonarOnce sync.Once
|
||||
sonarClient *sonargo.Client
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&sonarAddress, "sonar-address", "", "sonar server host")
|
||||
flag.StringVar(&sonarToken, "sonar-token", "", "sonar token")
|
||||
}
|
||||
|
||||
func Client() *sonargo.Client {
|
||||
|
||||
sonarOnce.Do(func() {
|
||||
if sonarAddress == "" {
|
||||
sonarClient = nil
|
||||
glog.Info("skip sonar init")
|
||||
return
|
||||
}
|
||||
if !strings.HasSuffix(sonarAddress, "/") {
|
||||
sonarAddress += "/"
|
||||
}
|
||||
client, err := sonargo.NewClientWithToken(sonarAddress+"api/", sonarToken)
|
||||
if err != nil {
|
||||
glog.Error("failed to connect to sonar")
|
||||
return
|
||||
}
|
||||
_, _, err = client.Projects.Search(nil)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to search sonar projects [%+v]", err)
|
||||
return
|
||||
}
|
||||
glog.Info("init sonar client success")
|
||||
sonarClient = client
|
||||
})
|
||||
|
||||
return sonarClient
|
||||
}
|
||||
54
pkg/simple/client/sonarqube/sonarqube.go
Normal file
54
pkg/simple/client/sonarqube/sonarqube.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package sonarqube
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kubesphere/sonargo/sonar"
|
||||
"k8s.io/klog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SonarQubeClient struct {
|
||||
client *sonargo.Client
|
||||
}
|
||||
|
||||
func NewSonarQubeClient(options *SonarQubeOptions) (*SonarQubeClient, error) {
|
||||
var endpoint string
|
||||
|
||||
if strings.HasSuffix(options.Host, "/") {
|
||||
endpoint = fmt.Sprintf("%sapi/", options.Host)
|
||||
} else {
|
||||
endpoint = fmt.Sprintf("%s/api/", options.Host)
|
||||
}
|
||||
|
||||
sonar, err := sonargo.NewClientWithToken(endpoint, options.Token)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to connect to sonarqube service, %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SonarQubeClient{client: sonar}, err
|
||||
}
|
||||
|
||||
func NewSonarQubeClientOrDie(options *SonarQubeOptions) *SonarQubeClient {
|
||||
var endpoint string
|
||||
|
||||
if strings.HasSuffix(options.Host, "/") {
|
||||
endpoint = fmt.Sprintf("%sapi/", options.Host)
|
||||
} else {
|
||||
endpoint = fmt.Sprintf("%s/api/", options.Host)
|
||||
}
|
||||
|
||||
sonar, err := sonargo.NewClientWithToken(endpoint, options.Token)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to connect to sonarqube service, %+v", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &SonarQubeClient{client: sonar}
|
||||
}
|
||||
|
||||
// return sonarqube client
|
||||
// Also we can wrap some methods to avoid direct use sonar client
|
||||
func (s *SonarQubeClient) SonarQube() *sonargo.Client {
|
||||
return s.client
|
||||
}
|
||||
Reference in New Issue
Block a user