From 54ada6b8871b0d2c1ee8a59db72bc396abd3af61 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Sun, 10 Nov 2019 03:30:09 +0800 Subject: [PATCH] feature: keystone add service config API and allow config services by API --- cmd/climc/shell/services.go | 69 +++++++++ pkg/apis/compute/consts.go | 20 +++ pkg/apis/compute/reservedip.go | 14 ++ pkg/apis/identity/config.go | 2 +- pkg/apis/identity/consts.go | 33 ++++- pkg/apis/identity/resource.go | 21 +++ pkg/apis/image/consts.go | 3 +- pkg/cloudcommon/app/mergeconf.go | 97 ++++++++++++ pkg/cloudcommon/consts/consts.go | 2 + pkg/compute/models/skus_tools.go | 14 ++ pkg/compute/service/service.go | 10 +- pkg/image/service/service.go | 5 + pkg/keystone/driver/base.go | 6 +- pkg/keystone/driver/cas/cas.go | 2 +- pkg/keystone/driver/cas/class.go | 2 +- pkg/keystone/driver/driver.go | 2 +- pkg/keystone/driver/ldap/class.go | 2 +- pkg/keystone/driver/ldap/ldap.go | 2 +- pkg/keystone/driver/sql/class.go | 2 +- pkg/keystone/driver/sql/sql.go | 2 +- pkg/keystone/models/configs.go | 181 ++++++++++++++++++++--- pkg/keystone/models/identity_provider.go | 56 ++----- pkg/keystone/models/passwords.go | 4 +- pkg/keystone/models/services.go | 58 ++++++++ pkg/keystone/models/syncworker.go | 2 +- pkg/keystone/options/options.go | 4 +- pkg/keystone/service/service.go | 7 + pkg/keystone/tokens/auth.go | 4 +- pkg/mcclient/modules/k8s/rbac.go | 14 ++ pkg/mcclient/options/k8s/job.go | 14 ++ pkg/mcclient/options/k8s/pod_template.go | 14 ++ pkg/mcclient/options/k8s/statefulset.go | 14 ++ 32 files changed, 595 insertions(+), 87 deletions(-) create mode 100644 pkg/apis/compute/consts.go create mode 100644 pkg/apis/identity/resource.go create mode 100644 pkg/cloudcommon/app/mergeconf.go diff --git a/cmd/climc/shell/services.go b/cmd/climc/shell/services.go index 189cfe3d41..180ae8b8ca 100644 --- a/cmd/climc/shell/services.go +++ b/cmd/climc/shell/services.go @@ -15,10 +15,14 @@ package shell import ( + "fmt" + "strings" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/util/fileutils2" ) func init() { @@ -144,4 +148,69 @@ func init() { printObject(srv) return nil }) + + type ServiceConfigShowOptions struct { + SERVICE string `help:"service name or id"` + } + R(&ServiceConfigShowOptions{}, "service-config-show", "Show configs of a service", func(s *mcclient.ClientSession, args *ServiceConfigShowOptions) error { + conf, err := modules.ServicesV3.GetSpecific(s, args.SERVICE, "config", nil) + if err != nil { + return err + } + fmt.Println(conf.PrettyString()) + return nil + }) + + type ServiceConfigOptions struct { + SERVICE string `help:"service name or id"` + Config []string `help:"config key=value pair"` + Remove bool `help:"remove config"` + } + R(&ServiceConfigOptions{}, "service-config", "Add config to service", func(s *mcclient.ClientSession, args *ServiceConfigOptions) error { + config := jsonutils.NewDict() + if args.Remove { + config.Add(jsonutils.NewString("remove"), "action") + } else { + config.Add(jsonutils.NewString("update"), "action") + } + for _, c := range args.Config { + pos := strings.IndexByte(c, '=') + if pos < 0 { + return fmt.Errorf("%s is not a key=value pair", c) + } + key := strings.TrimSpace(c[:pos]) + value := strings.TrimSpace(c[pos+1:]) + config.Add(jsonutils.NewString(value), "config", "default", key) + } + nconf, err := modules.ServicesV3.PerformAction(s, args.SERVICE, "config", config) + if err != nil { + return err + } + fmt.Println(nconf.PrettyString()) + return nil + }) + + type ServiceConfigYamlOptions struct { + SERVICE string `help:"service name or id"` + YAML string `help:"config yaml file"` + } + R(&ServiceConfigYamlOptions{}, "service-config-yaml", "Config service with a yaml file", func(s *mcclient.ClientSession, args *ServiceConfigYamlOptions) error { + content, err := fileutils2.FileGetContents(args.YAML) + if err != nil { + return err + } + yamlJson, err := jsonutils.ParseYAML(content) + if err != nil { + return err + } + config := jsonutils.NewDict() + config.Add(yamlJson, "config", "default") + nconf, err := modules.ServicesV3.PerformAction(s, args.SERVICE, "config", config) + if err != nil { + return err + } + fmt.Println(nconf.PrettyString()) + return nil + }) + } diff --git a/pkg/apis/compute/consts.go b/pkg/apis/compute/consts.go new file mode 100644 index 0000000000..df9535bf97 --- /dev/null +++ b/pkg/apis/compute/consts.go @@ -0,0 +1,20 @@ +// Copyright 2019 Yunion +// +// 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 compute + +const ( + SERVICE_TYPE = "compute" + SERVICE_VERSION = "v2" +) diff --git a/pkg/apis/compute/reservedip.go b/pkg/apis/compute/reservedip.go index 57c8d73cda..babe324d01 100644 --- a/pkg/apis/compute/reservedip.go +++ b/pkg/apis/compute/reservedip.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 compute const ( diff --git a/pkg/apis/identity/config.go b/pkg/apis/identity/config.go index 400f4a8118..6ad51de9a0 100644 --- a/pkg/apis/identity/config.go +++ b/pkg/apis/identity/config.go @@ -21,7 +21,7 @@ const ( QUeryScopeSub = "sub" ) -type TIdentityProviderConfigs map[string]map[string]jsonutils.JSONObject +type TConfigs map[string]map[string]jsonutils.JSONObject type SLDAPIdpConfigBaseOptions struct { Url string `json:"url,omitempty" help:"LDAP server URL" required:"true"` diff --git a/pkg/apis/identity/consts.go b/pkg/apis/identity/consts.go index 7b8ec5a98c..1b360c247c 100644 --- a/pkg/apis/identity/consts.go +++ b/pkg/apis/identity/consts.go @@ -83,7 +83,36 @@ var ( IdentityDriverLDAP, } - SensitiveDomainConfigMap = map[string]string{ - "ldap": "password", + SensitiveDomainConfigMap = map[string][]string{ + "ldap": []string{ + "password", + }, + } + + BlacklistOptionMap = map[string][]string{ + "default": []string{ + "region", + "sql_connection", + "config", + "application_id", + "log_level", + "temp_path", + "auto_sync_table", + "address", + "port", + "admin_port", + "notify_admin_users", + "session_endpoint_type", + "admin_password", + "admin_project", + "admin_user", + "auth_url", + "default_aws_instance_type_file", + "port_v2", + "enable_ssl", + "ssl_certfile", + "ssl_keyfile", + "ssl_ca_certs", + }, } ) diff --git a/pkg/apis/identity/resource.go b/pkg/apis/identity/resource.go new file mode 100644 index 0000000000..8c06dec1d7 --- /dev/null +++ b/pkg/apis/identity/resource.go @@ -0,0 +1,21 @@ +// Copyright 2019 Yunion +// +// 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 identity + +const ( + IDENTITY_PROVIDER_TABLE = "identity_provider" + IDENTITY_PROVIDER_RESOURCE_TYPE = "identity_provider" + IDENTITY_PROVIDER_RESOURCE_TYPES = "identity_providers" +) diff --git a/pkg/apis/image/consts.go b/pkg/apis/image/consts.go index 447a4c5436..5bfa3e1b07 100644 --- a/pkg/apis/image/consts.go +++ b/pkg/apis/image/consts.go @@ -17,7 +17,8 @@ package image type TImageType string const ( - SERVICE_TYPE = "image" + SERVICE_TYPE = "image" + SERVICE_VERSION = "" // https://docs.openstack.org/glance/pike/user/statuses.html // diff --git a/pkg/cloudcommon/app/mergeconf.go b/pkg/cloudcommon/app/mergeconf.go new file mode 100644 index 0000000000..273e21843d --- /dev/null +++ b/pkg/cloudcommon/app/mergeconf.go @@ -0,0 +1,97 @@ +// Copyright 2019 Yunion +// +// 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 app + +import ( + "context" + "database/sql" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/cloudcommon/consts" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +func getServiceIdByType(s *mcclient.ClientSession, typeStr string, verStr string) (string, error) { + params := jsonutils.NewDict() + if len(verStr) > 0 { + typeStr += "_" + verStr + } + params.Add(jsonutils.NewString(typeStr), "type") + result, err := modules.ServicesV3.List(s, params) + if err != nil { + return "", errors.Wrap(err, "modules.ServicesV3.List") + } + if len(result.Data) == 0 { + return "", errors.Wrap(sql.ErrNoRows, "modules.ServicesV3.List") + } else if len(result.Data) > 1 { + return "", errors.Wrap(sqlchemy.ErrDuplicateEntry, "modules.ServicesV3.List") + } + return result.Data[0].GetString("id") +} + +func getServiceConfig(s *mcclient.ClientSession, serviceId string) (jsonutils.JSONObject, error) { + conf, err := modules.ServicesV3.GetSpecific(s, serviceId, "config", nil) + if err != nil { + return nil, errors.Wrap(err, "modules.ServicesV3.GetSpecific config") + } + defConf, _ := conf.Get("config", "default") + return defConf, nil +} + +func MergeServiceConfig(opts interface{}, serviceType string, serviceVersion string) error { + merged := false + conf := jsonutils.Marshal(opts).(*jsonutils.JSONDict) + region, _ := conf.GetString("region") + epType, _ := conf.GetString("session_endpoint_type") + s := auth.AdminSession(context.Background(), region, "", epType, "") + serviceId, _ := getServiceIdByType(s, serviceType, serviceVersion) + if len(serviceId) > 0 { + serviceConf, err := getServiceConfig(s, serviceId) + if err != nil { + return errors.Wrap(err, "getServiceConfig") + } + conf.Update(serviceConf) + merged = true + } + commonServiceId, _ := getServiceIdByType(s, consts.COMMON_SERVICE, "") + if len(commonServiceId) > 0 { + commonConf, err := getServiceConfig(s, commonServiceId) + if err != nil { + return errors.Wrap(err, "getServiceConfig common service") + } + conf.Update(commonConf) + merged = true + } + if merged { + err := conf.Unmarshal(opts) + if err != nil { + return errors.Wrap(err, "conf.Unmarshal") + } + if len(serviceId) > 0 { + nconf := jsonutils.NewDict() + nconf.Add(conf, "config", "default") + _, err := modules.ServicesV3.PerformAction(s, serviceId, "config", nconf) + if err != nil { + return errors.Wrap(err, "modules.ServicesV3.PerformAction") + } + } + } + return nil +} diff --git a/pkg/cloudcommon/consts/consts.go b/pkg/cloudcommon/consts/consts.go index b59a671d15..df23cf8c1f 100644 --- a/pkg/cloudcommon/consts/consts.go +++ b/pkg/cloudcommon/consts/consts.go @@ -19,6 +19,8 @@ import ( ) var ( + COMMON_SERVICE = "common" + globalRegion = "" globalServiceType = "" diff --git a/pkg/compute/models/skus_tools.go b/pkg/compute/models/skus_tools.go index 5dd18d53b0..d0a59ecfef 100644 --- a/pkg/compute/models/skus_tools.go +++ b/pkg/compute/models/skus_tools.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 models import ( diff --git a/pkg/compute/service/service.go b/pkg/compute/service/service.go index 84c9a96789..6108502c2f 100644 --- a/pkg/compute/service/service.go +++ b/pkg/compute/service/service.go @@ -22,6 +22,7 @@ import ( "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon" app_common "yunion.io/x/onecloud/pkg/cloudcommon/app" "yunion.io/x/onecloud/pkg/cloudcommon/cronman" @@ -43,7 +44,7 @@ func StartService() { commonOpts := &options.Options.CommonOptions baseOpts := &options.Options.BaseOptions dbOpts := &options.Options.DBOptions - common_options.ParseOptions(opts, os.Args, "region.conf", "compute") + common_options.ParseOptions(opts, os.Args, "region.conf", api.SERVICE_TYPE) if opts.PortV2 > 0 { log.Infof("Port V2 %d is specified, use v2 port", opts.PortV2) @@ -62,7 +63,12 @@ func StartService() { db.EnsureAppInitSyncDB(app, dbOpts, models.InitDB) defer cloudcommon.CloseDB() - err := setInfluxdbRetentionPolicy() + err := app_common.MergeServiceConfig(opts, api.SERVICE_TYPE, api.SERVICE_VERSION) + if err != nil { + log.Fatalf("Fail to merge service config %s", err) + } + + err = setInfluxdbRetentionPolicy() if err != nil { log.Errorf("setInfluxdbRetentionPolicy fail: %s", err) } diff --git a/pkg/image/service/service.go b/pkg/image/service/service.go index ab7db5749d..943ea3ad42 100644 --- a/pkg/image/service/service.go +++ b/pkg/image/service/service.go @@ -93,6 +93,11 @@ func StartService() { db.EnsureAppInitSyncDB(app, dbOpts, models.InitDB) + err := app_common.MergeServiceConfig(opts, api.SERVICE_TYPE, api.SERVICE_VERSION) + if err != nil { + log.Fatalf("Fail to merge service config %s", err) + } + go models.CheckImages() if len(options.Options.DeployServerSocketPath) > 0 { diff --git a/pkg/keystone/driver/base.go b/pkg/keystone/driver/base.go index 18112abe23..3704b09511 100644 --- a/pkg/keystone/driver/base.go +++ b/pkg/keystone/driver/base.go @@ -34,7 +34,7 @@ func GetDriverClass(drv string) IIdentityBackendClass { return nil } -func GetDriver(driver string, idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (IIdentityBackend, error) { +func GetDriver(driver string, idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (IIdentityBackend, error) { drvCls := GetDriverClass(driver) if drvCls == nil { return nil, ErrNoSuchDriver @@ -45,7 +45,7 @@ func GetDriver(driver string, idpId, idpName, template, targetDomainId string, a type SBaseIdentityDriver struct { object.SObject - Config api.TIdentityProviderConfigs + Config api.TConfigs IdpId string IdpName string Template string @@ -58,7 +58,7 @@ func (base *SBaseIdentityDriver) IIdentityBackend() IIdentityBackend { return base.GetVirtualObject().(IIdentityBackend) } -func NewBaseIdentityDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (SBaseIdentityDriver, error) { +func NewBaseIdentityDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (SBaseIdentityDriver, error) { drv := SBaseIdentityDriver{} drv.IdpId = idpId drv.IdpName = idpName diff --git a/pkg/keystone/driver/cas/cas.go b/pkg/keystone/driver/cas/cas.go index 193630fe7d..d8ad061956 100644 --- a/pkg/keystone/driver/cas/cas.go +++ b/pkg/keystone/driver/cas/cas.go @@ -40,7 +40,7 @@ type SCASDriver struct { isDebug bool } -func NewCASDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) { +func NewCASDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (driver.IIdentityBackend, error) { base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf) if err != nil { return nil, errors.Wrap(err, "NewBaseIdentityDriver") diff --git a/pkg/keystone/driver/cas/class.go b/pkg/keystone/driver/cas/class.go index 1ca4ee9068..473db9cf60 100644 --- a/pkg/keystone/driver/cas/class.go +++ b/pkg/keystone/driver/cas/class.go @@ -29,7 +29,7 @@ func (self *SCASDriverClass) SyncMethod() string { return api.IdentityProviderSyncOnAuth } -func (self *SCASDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) { +func (self *SCASDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (driver.IIdentityBackend, error) { return NewCASDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf) } diff --git a/pkg/keystone/driver/driver.go b/pkg/keystone/driver/driver.go index 4398a87e27..6fa44e8335 100644 --- a/pkg/keystone/driver/driver.go +++ b/pkg/keystone/driver/driver.go @@ -25,7 +25,7 @@ type IIdentityBackendClass interface { SingletonInstance() bool SyncMethod() string Name() string - NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (IIdentityBackend, error) + NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (IIdentityBackend, error) } type IIdentityBackend interface { diff --git a/pkg/keystone/driver/ldap/class.go b/pkg/keystone/driver/ldap/class.go index 0b0916b22a..7a6c80e220 100644 --- a/pkg/keystone/driver/ldap/class.go +++ b/pkg/keystone/driver/ldap/class.go @@ -29,7 +29,7 @@ func (self *SLDAPDriverClass) SyncMethod() string { return api.IdentityProviderSyncFull } -func (self *SLDAPDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) { +func (self *SLDAPDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (driver.IIdentityBackend, error) { return NewLDAPDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf) } diff --git a/pkg/keystone/driver/ldap/ldap.go b/pkg/keystone/driver/ldap/ldap.go index dc18b95cae..9b0e9faaa6 100644 --- a/pkg/keystone/driver/ldap/ldap.go +++ b/pkg/keystone/driver/ldap/ldap.go @@ -39,7 +39,7 @@ type SLDAPDriver struct { ldapConfig *api.SLDAPIdpConfigOptions } -func NewLDAPDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) { +func NewLDAPDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (driver.IIdentityBackend, error) { base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf) if err != nil { return nil, errors.Wrap(err, "NewBaseIdentityDriver") diff --git a/pkg/keystone/driver/sql/class.go b/pkg/keystone/driver/sql/class.go index 9b6bda2a71..5297f6db71 100644 --- a/pkg/keystone/driver/sql/class.go +++ b/pkg/keystone/driver/sql/class.go @@ -29,7 +29,7 @@ func (self *SSQLDriverClass) SyncMethod() string { return api.IdentityProviderSyncLocal } -func (self *SSQLDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) { +func (self *SSQLDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (driver.IIdentityBackend, error) { return NewSQLDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf) } diff --git a/pkg/keystone/driver/sql/sql.go b/pkg/keystone/driver/sql/sql.go index a62f718703..0cde7e5b54 100644 --- a/pkg/keystone/driver/sql/sql.go +++ b/pkg/keystone/driver/sql/sql.go @@ -30,7 +30,7 @@ type SSQLDriver struct { driver.SBaseIdentityDriver } -func NewSQLDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) { +func NewSQLDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TConfigs) (driver.IIdentityBackend, error) { base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf) if err != nil { return nil, err diff --git a/pkg/keystone/models/configs.go b/pkg/keystone/models/configs.go index f299ed517b..fe93f23582 100644 --- a/pkg/keystone/models/configs.go +++ b/pkg/keystone/models/configs.go @@ -15,21 +15,22 @@ package models import ( - "context" "database/sql" "sort" - "github.com/pkg/errors" - "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/identity" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/cloudcommon/db" - "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/keystone/options" ) type SConfigOptionManager struct { db.SResourceBaseManager + IsSensitive bool } var ( @@ -45,6 +46,7 @@ func init() { "sensitive_config", "sensitive_configs", ), + IsSensitive: true, } SensitiveConfigManager.SetVirtualObject(SensitiveConfigManager) WhitelistedConfigManager = &SConfigOptionManager{ @@ -54,6 +56,7 @@ func init() { "whitelisted_config", "whitelisted_configs", ), + IsSensitive: false, } WhitelistedConfigManager.SetVirtualObject(WhitelistedConfigManager) } @@ -72,20 +75,29 @@ func init() { type SConfigOption struct { db.SResourceBase - IdpId string `name:"domain_id" width:"64" charset:"ascii" primary:"true"` - Group string `width:"255" charset:"utf8" primary:"true"` - Option string `width:"255" charset:"utf8" primary:"true"` + ResType string `width:"32" charset:"ascii" nullable:"false" default:"identity_provider" primary:"true"` + ResId string `name:"domain_id" width:"64" charset:"ascii" primary:"true"` + Group string `width:"255" charset:"utf8" primary:"true"` + Option string `width:"255" charset:"utf8" primary:"true"` Value jsonutils.JSONObject `nullable:"false"` } -func (manager *SConfigOptionManager) fetchConfigs(idpId string, groups []string, options []string) (TConfigOptions, error) { - q := manager.Query().Equals("domain_id", idpId) +func (manager *SConfigOptionManager) fetchConfigs(model db.IModel, groups []string, options []string) (TConfigOptions, error) { + q := manager.Query().Equals("res_type", model.Keyword()).Equals("domain_id", model.GetId()) if len(groups) > 0 { - q = q.In("group", groups) + if len(groups) == 1 { + q = q.Equals("group", groups[0]) + } else { + q = q.In("group", groups) + } } if len(options) > 0 { - q = q.In("option", options) + if len(options) == 1 { + q = q.Equals("option", options[0]) + } else { + q = q.In("option", options) + } } opts := make(TConfigOptions, 0) err := db.FetchModelObjects(manager, q, &opts) @@ -96,8 +108,8 @@ func (manager *SConfigOptionManager) fetchConfigs(idpId string, groups []string, return opts, nil } -func config2map(opts []SConfigOption) api.TIdentityProviderConfigs { - conf := make(api.TIdentityProviderConfigs) +func config2map(opts []SConfigOption) api.TConfigs { + conf := make(api.TConfigs) for i := range opts { opt := opts[i] if _, ok := conf[opt.Group]; !ok { @@ -108,12 +120,39 @@ func config2map(opts []SConfigOption) api.TIdentityProviderConfigs { return conf } -func (manager *SConfigOptionManager) deleteConfig(ctx context.Context, userCred mcclient.TokenCredential, idpId string) error { - return manager.syncConfig(ctx, userCred, idpId, nil) +func (manager *SConfigOptionManager) deleteConfigs(model db.IModel) error { + return manager.syncConfigs(model, nil) } -func (manager *SConfigOptionManager) syncConfig(ctx context.Context, userCred mcclient.TokenCredential, idpId string, newOpts TConfigOptions) error { - oldOpts, err := manager.fetchConfigs(idpId, nil, nil) +func (manager *SConfigOptionManager) updateConfigs(newOpts TConfigOptions) error { + for i := range newOpts { + err := manager.TableSpec().InsertOrUpdate(&newOpts[i]) + if err != nil { + return errors.Wrap(err, "Insert") + } + } + return nil +} + +func (manager *SConfigOptionManager) removeConfigs(model db.IModel, newOpts TConfigOptions) error { + oldOpts, err := manager.fetchConfigs(model, nil, nil) + if err != nil { + return errors.Wrap(err, "fetchOldConfigs") + } + _, updated1, _, _ := compareConfigOptions(oldOpts, newOpts) + for i := range updated1 { + _, err := db.Update(&updated1[i], func() error { + return updated1[i].MarkDelete() + }) + if err != nil { + return errors.Wrap(err, "Delete") + } + } + return nil +} + +func (manager *SConfigOptionManager) syncConfigs(model db.IModel, newOpts TConfigOptions) error { + oldOpts, err := manager.fetchConfigs(model, nil, nil) if err != nil { return errors.Wrap(err, "fetchOldConfigs") } @@ -144,17 +183,20 @@ func (manager *SConfigOptionManager) syncConfig(ctx context.Context, userCred mc return nil } -func getConfigOptions(conf api.TIdentityProviderConfigs, idpId string, sensitiveList map[string]string) (TConfigOptions, TConfigOptions) { +func getConfigOptions(conf api.TConfigs, model db.IModel, blackList map[string][]string, sensitiveList map[string][]string) (TConfigOptions, TConfigOptions) { options := make(TConfigOptions, 0) sensitive := make(TConfigOptions, 0) for group, groupConf := range conf { for optKey, optVal := range groupConf { opt := SConfigOption{} - opt.IdpId = idpId + opt.ResType = model.Keyword() + opt.ResId = model.GetId() opt.Group = group opt.Option = optKey opt.Value = optVal - if v, ok := sensitiveList[group]; ok && v == optKey { + if v, ok := blackList[group]; ok && utils.IsInStringArray(optKey, v) { + // skip + } else if v, ok := sensitiveList[group]; ok && utils.IsInStringArray(optKey, v) { sensitive = append(sensitive, opt) } else { options = append(options, opt) @@ -227,8 +269,10 @@ func compareConfigOptions(opts1, opts2 TConfigOptions) (deleted, updated1, updat return } -func (manager *SConfigOptionManager) getDriver(idStr string) (string, error) { - opts, err := manager.fetchConfigs(idStr, []string{"identity"}, []string{"driver"}) +func (manager *SConfigOptionManager) getDriver(idpId string) (string, error) { + idp, _ := db.NewModelObject(IdentityProviderManager) + idp.(*SIdentityProvider).Id = idpId + opts, err := manager.fetchConfigs(idp, []string{"identity"}, []string{"driver"}) if err != nil { return "", errors.Wrap(err, "WhitelistedConfigManager.fetchConfigs") } @@ -237,3 +281,96 @@ func (manager *SConfigOptionManager) getDriver(idStr string) (string, error) { } return api.IdentityDriverSQL, nil } + +func GetConfigs(model db.IModel, all bool) (api.TConfigs, error) { + opts, err := WhitelistedConfigManager.fetchConfigs(model, nil, nil) + if err != nil { + return nil, err + } + if all { + opts2, err := SensitiveConfigManager.fetchConfigs(model, nil, nil) + if err != nil { + return nil, err + } + opts = append(opts, opts2...) + } + return config2map(opts), nil +} + +func saveConfigs(action string, model db.IModel, opts api.TConfigs, blackList map[string][]string, sensitiveConfs map[string][]string) error { + whiteListedOpts, sensitiveOpts := getConfigOptions(opts, model, blackList, sensitiveConfs) + if action == "update" { + err := WhitelistedConfigManager.updateConfigs(whiteListedOpts) + if err != nil { + return errors.Wrap(err, "WhitelistedConfigManager.updateConfig") + } + err = SensitiveConfigManager.updateConfigs(sensitiveOpts) + if err != nil { + return errors.Wrap(err, "SensitiveConfigManager.updateConfig") + } + } else if action == "remove" { + err := WhitelistedConfigManager.removeConfigs(model, whiteListedOpts) + if err != nil { + return errors.Wrap(err, "WhitelistedConfigManager.updateConfig") + } + err = SensitiveConfigManager.removeConfigs(model, sensitiveOpts) + if err != nil { + return errors.Wrap(err, "SensitiveConfigManager.updateConfig") + } + } else { + err := WhitelistedConfigManager.syncConfigs(model, whiteListedOpts) + if err != nil { + return errors.Wrap(err, "WhitelistedConfigManager.syncConfig") + } + err = SensitiveConfigManager.syncConfigs(model, sensitiveOpts) + if err != nil { + return errors.Wrap(err, "SensitiveConfigManager.syncConfig") + } + } + return nil +} + +func MergeServiceConfig(opts *options.SKeystoneOptions) error { + merged := false + conf := jsonutils.Marshal(opts).(*jsonutils.JSONDict) + service, _ := ServiceManager.fetchServiceByType(api.SERVICE_TYPE) + if service != nil { + serviceConf, err := GetConfigs(service, false) + if err != nil { + return errors.Wrap(err, "GetConfigs service") + } + serviceConfJson := jsonutils.Marshal(serviceConf["default"]) + conf.Update(serviceConfJson) + merged = true + } + commonService, _ := ServiceManager.fetchServiceByType(consts.COMMON_SERVICE) + if commonService != nil { + commonConf, err := GetConfigs(commonService, false) + if err != nil { + return errors.Wrap(err, "GetConfigs commonService") + } + commonConfJson := jsonutils.Marshal(commonConf["default"]) + conf.Update(commonConfJson) + merged = true + } + if merged { + err := conf.Unmarshal(opts) + if err != nil { + return errors.Wrap(err, "conf.Unmarshal") + } + if service != nil { + nconf := jsonutils.NewDict() + nconf.Add(conf, "default") + tconf := api.TConfigs{} + err = nconf.Unmarshal(tconf) + if err != nil { + return errors.Wrap(err, "conf.Unmarshal(tconf)") + } + err = saveConfigs("", service, tconf, api.BlacklistOptionMap, nil) + if err != nil { + return errors.Wrap(err, "saveConfigs") + } + } + } + return nil +} diff --git a/pkg/keystone/models/identity_provider.go b/pkg/keystone/models/identity_provider.go index e05d35670b..b50895f878 100644 --- a/pkg/keystone/models/identity_provider.go +++ b/pkg/keystone/models/identity_provider.go @@ -50,9 +50,9 @@ func init() { IdentityProviderManager = &SIdentityProviderManager{ SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager( SIdentityProvider{}, - "identity_provider", - "identity_provider", - "identity_providers", + api.IDENTITY_PROVIDER_TABLE, + api.IDENTITY_PROVIDER_RESOURCE_TYPE, + api.IDENTITY_PROVIDER_RESOURCE_TYPES, ), } IdentityProviderManager.SetVirtualObject(IdentityProviderManager) @@ -201,7 +201,7 @@ func (self *SIdentityProvider) AllowGetDetailsConfig(ctx context.Context, userCr } func (self *SIdentityProvider) GetDetailsConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { - conf, err := self.GetConfig(false) + conf, err := GetConfigs(self, false) if err != nil { return nil, err } @@ -210,21 +210,6 @@ func (self *SIdentityProvider) GetDetailsConfig(ctx context.Context, userCred mc return result, nil } -func (self *SIdentityProvider) GetConfig(all bool) (api.TIdentityProviderConfigs, error) { - opts, err := WhitelistedConfigManager.fetchConfigs(self.Id, nil, nil) - if err != nil { - return nil, err - } - if all { - opts2, err := SensitiveConfigManager.fetchConfigs(self.Id, nil, nil) - if err != nil { - return nil, err - } - opts = append(opts, opts2...) - } - return config2map(opts), nil -} - func (ident *SIdentityProvider) AllowPerformConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) bool { return db.IsAdminAllowUpdateSpec(userCred, ident, "config") } @@ -236,13 +221,13 @@ func (ident *SIdentityProvider) PerformConfig(ctx context.Context, userCred mccl if ident.SyncStatus != api.IdentitySyncStatusIdle { return nil, httperrors.NewInvalidStatusError("cannot update config when not idle") } - opts := api.TIdentityProviderConfigs{} + opts := api.TConfigs{} err := data.Unmarshal(&opts, "config") if err != nil { return nil, httperrors.NewInputParameterError("invalid input data") } - - err = ident.saveConfig(ctx, userCred, opts) + action, _ := data.GetString("action") + err = saveConfigs(action, ident, opts, nil, api.SensitiveDomainConfigMap) if err != nil { return nil, httperrors.NewInternalServerError("saveConfig fail %s", err) } @@ -251,19 +236,6 @@ func (ident *SIdentityProvider) PerformConfig(ctx context.Context, userCred mccl return ident.GetDetailsConfig(ctx, userCred, query) } -func (ident *SIdentityProvider) saveConfig(ctx context.Context, userCred mcclient.TokenCredential, opts api.TIdentityProviderConfigs) error { - whiteListedOpts, sensitiveOpts := getConfigOptions(opts, ident.Id, api.SensitiveDomainConfigMap) - err := WhitelistedConfigManager.syncConfig(ctx, userCred, ident.Id, whiteListedOpts) - if err != nil { - return errors.Wrap(err, "WhitelistedConfigManager.syncConfig") - } - err = SensitiveConfigManager.syncConfig(ctx, userCred, ident.Id, sensitiveOpts) - if err != nil { - return errors.Wrap(err, "SensitiveConfigManager.syncConfig") - } - return nil -} - func (manager *SIdentityProviderManager) getDriveInstanceCount(drvName string) (int, error) { return manager.Query().Equals("driver", drvName).CountWithError() } @@ -320,7 +292,7 @@ func (manager *SIdentityProviderManager) ValidateCreateData(ctx context.Context, data.Set("target_domain_id", jsonutils.NewString(domain.Id)) } - opts := api.TIdentityProviderConfigs{} + opts := api.TConfigs{} err := data.Unmarshal(&opts, "config") if err != nil { return nil, httperrors.NewInputParameterError("parse config error: %s", err) @@ -338,13 +310,13 @@ func (ident *SIdentityProvider) PostCreate(ctx context.Context, userCred mcclien logclient.AddActionLogWithContext(ctx, ident, logclient.ACT_CREATE, data, userCred, true) - opts := api.TIdentityProviderConfigs{} + opts := api.TConfigs{} err := data.Unmarshal(&opts, "config") if err != nil { log.Errorf("parse config error %s", err) return } - err = ident.saveConfig(ctx, userCred, opts) + err = saveConfigs("", ident, opts, nil, api.SensitiveDomainConfigMap) if err != nil { log.Errorf("saveConfig fail %s", err) return @@ -611,12 +583,12 @@ func (self *SIdentityProvider) getDomains() ([]SDomain, error) { return domains, nil } -func (ident *SIdentityProvider) deleteConfig(ctx context.Context, userCred mcclient.TokenCredential) error { - err := WhitelistedConfigManager.deleteConfig(ctx, userCred, ident.Id) +func (ident *SIdentityProvider) deleteConfigs(ctx context.Context, userCred mcclient.TokenCredential) error { + err := WhitelistedConfigManager.deleteConfigs(ident) if err != nil { return errors.Wrap(err, "WhitelistedConfigManager.deleteConfig") } - err = SensitiveConfigManager.deleteConfig(ctx, userCred, ident.Id) + err = SensitiveConfigManager.deleteConfigs(ident) if err != nil { return errors.Wrap(err, "SensitiveConfigManager.deleteConfig") } @@ -642,7 +614,7 @@ func (self *SIdentityProvider) Purge(ctx context.Context, userCred mcclient.Toke return errors.Wrap(err, "delete domain") } } - err = self.deleteConfig(ctx, userCred) + err = self.deleteConfigs(ctx, userCred) if err != nil { return errors.Wrap(err, "self.deleteConfig") } diff --git a/pkg/keystone/models/passwords.go b/pkg/keystone/models/passwords.go index ae022cce9c..f10db6d32c 100644 --- a/pkg/keystone/models/passwords.go +++ b/pkg/keystone/models/passwords.go @@ -140,8 +140,8 @@ func (manager *SPasswordManager) savePassword(localUserId int, password string, rec.Password = shaPassword(password) now := time.Now() rec.CreatedAtInt = now.UnixNano() / 1000 - if o.Options.PasswordExpirationDays > 0 && !isSystemAccount { - rec.ExpiresAt = now.Add(24 * time.Hour * time.Duration(o.Options.PasswordExpirationDays)) + if o.Options.PasswordExpirationSeconds > 0 && !isSystemAccount { + rec.ExpiresAt = now.Add(time.Second * time.Duration(o.Options.PasswordExpirationSeconds)) rec.ExpiresAtInt = rec.ExpiresAt.UnixNano() / 1000 } err = manager.TableSpec().Insert(&rec) diff --git a/pkg/keystone/models/services.go b/pkg/keystone/models/services.go index 68e0a432dc..15ce42109e 100644 --- a/pkg/keystone/models/services.go +++ b/pkg/keystone/models/services.go @@ -16,10 +16,14 @@ package models import ( "context" + "database/sql" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/tristate" + "yunion.io/x/sqlchemy" + api "yunion.io/x/onecloud/pkg/apis/identity" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" @@ -136,3 +140,57 @@ func (service *SService) PostDelete(ctx context.Context, userCred mcclient.Token service.SStandaloneResourceBase.PostDelete(ctx, userCred) logclient.AddActionLogWithContext(ctx, service, logclient.ACT_DELETE, nil, userCred, true) } + +func (service *SService) AllowGetDetailsConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowGetSpec(userCred, service, "config") +} + +func (service *SService) GetDetailsConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { + conf, err := GetConfigs(service, false) + if err != nil { + return nil, err + } + result := jsonutils.NewDict() + result.Add(jsonutils.Marshal(conf), "config") + return result, nil +} + +func (service *SService) AllowPerformConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) bool { + return db.IsAdminAllowUpdateSpec(userCred, service, "config") +} + +func (service *SService) PerformConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (jsonutils.JSONObject, error) { + action, _ := data.GetString("action") + opts := api.TConfigs{} + err := data.Unmarshal(&opts, "config") + if err != nil { + return nil, httperrors.NewInputParameterError("invalid input data") + } + err = saveConfigs(action, service, opts, api.BlacklistOptionMap, nil) + if err != nil { + return nil, httperrors.NewInternalServerError("saveConfig fail %s", err) + } + return service.GetDetailsConfig(ctx, userCred, query) +} + +func (manager *SServiceManager) fetchServiceByType(typeStr string) (*SService, error) { + q := manager.Query().Equals("type", typeStr) + cnt, err := q.CountWithError() + if err != nil && errors.Cause(err) != sql.ErrNoRows { + return nil, errors.Wrap(err, "CountWithError") + } + if cnt == 0 { + return nil, sql.ErrNoRows + } else if cnt > 1 { + return nil, sqlchemy.ErrDuplicateEntry + } + srvObj, err := db.NewModelObject(manager) + if err != nil { + return nil, errors.Wrap(err, "db.NewModelObject") + } + err = q.First(srvObj) + if err != nil { + return nil, errors.Wrap(err, "q.First") + } + return srvObj.(*SService), nil +} diff --git a/pkg/keystone/models/syncworker.go b/pkg/keystone/models/syncworker.go index 5981d583a4..8a0cb92028 100644 --- a/pkg/keystone/models/syncworker.go +++ b/pkg/keystone/models/syncworker.go @@ -44,7 +44,7 @@ func submitIdpSyncTask(ctx context.Context, userCred mcclient.TokenCredential, i idp.SetSyncStatus(ctx, userCred, api.IdentitySyncStatusSyncing) defer idp.SetSyncStatus(ctx, userCred, api.IdentitySyncStatusIdle) - conf, err := idp.GetConfig(true) + conf, err := GetConfigs(idp, true) if err != nil { log.Errorf("GetConfig for idp %s fail %s", idp.Name, err) idp.MarkDisconnected(ctx, userCred) diff --git a/pkg/keystone/options/options.go b/pkg/keystone/options/options.go index 7fb405a4c2..de7ea12387 100644 --- a/pkg/keystone/options/options.go +++ b/pkg/keystone/options/options.go @@ -38,8 +38,8 @@ type SKeystoneOptions struct { FetchProjectResourceCountIntervalSeconds int `help:"frequency tp fetch project resource counts" default:"900"` - PasswordExpirationDays int `help:"password expires after the duration"` - PasswordMinimalLength int `help:"password minimal length"` + PasswordExpirationSeconds int `help:"password expires after the duration in seconds"` + PasswordMinimalLength int `help:"password minimal length" default:"6"` PasswordUniqueHistoryCheck int `help:"password must be unique in last N passwords"` PasswordErrorLockCount int `help:"lock user account if given number of failed auth"` diff --git a/pkg/keystone/service/service.go b/pkg/keystone/service/service.go index 6a90c38712..129d4e3e3a 100644 --- a/pkg/keystone/service/service.go +++ b/pkg/keystone/service/service.go @@ -21,6 +21,8 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/golang-plus/uuid" + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/identity" "yunion.io/x/onecloud/pkg/cloudcommon" app_common "yunion.io/x/onecloud/pkg/cloudcommon/app" @@ -74,6 +76,11 @@ func StartService() { app_common.InitBaseAuth(&opts.BaseOptions) + err := models.MergeServiceConfig(opts) + if err != nil { + log.Fatalf("Fail to merge service config: %s", err) + } + if !opts.IsSlaveNode { cron := cronman.InitCronJobManager(true, opts.CronJobWorkerCount) diff --git a/pkg/keystone/tokens/auth.go b/pkg/keystone/tokens/auth.go index f2b3a22057..80626ae3a9 100644 --- a/pkg/keystone/tokens/auth.go +++ b/pkg/keystone/tokens/auth.go @@ -150,7 +150,7 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent return nil, errors.Error(fmt.Sprintf("invalid idp status %s", idp.Status)) } - conf, err := idp.GetConfig(true) + conf, err := models.GetConfigs(idp, true) if err != nil { return nil, errors.Wrap(err, "GetConfig") } @@ -184,7 +184,7 @@ func authUserByCASV3(ctx context.Context, input mcclient.SAuthenticationInputV3) return nil, errors.Error("more than 1 cas identity providers?") } idp := &idps[0] - conf, err := idp.GetConfig(true) + conf, err := models.GetConfigs(idp, true) if err != nil { return nil, errors.Wrap(err, "idp.GetConfig") } diff --git a/pkg/mcclient/modules/k8s/rbac.go b/pkg/mcclient/modules/k8s/rbac.go index c3439995e7..a0fa00e374 100644 --- a/pkg/mcclient/modules/k8s/rbac.go +++ b/pkg/mcclient/modules/k8s/rbac.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 ( diff --git a/pkg/mcclient/options/k8s/job.go b/pkg/mcclient/options/k8s/job.go index 3132926b9e..3773ab9426 100644 --- a/pkg/mcclient/options/k8s/job.go +++ b/pkg/mcclient/options/k8s/job.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 ( diff --git a/pkg/mcclient/options/k8s/pod_template.go b/pkg/mcclient/options/k8s/pod_template.go index 2d9aafb04a..c884050540 100644 --- a/pkg/mcclient/options/k8s/pod_template.go +++ b/pkg/mcclient/options/k8s/pod_template.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 ( diff --git a/pkg/mcclient/options/k8s/statefulset.go b/pkg/mcclient/options/k8s/statefulset.go index b36178adf2..e7277819da 100644 --- a/pkg/mcclient/options/k8s/statefulset.go +++ b/pkg/mcclient/options/k8s/statefulset.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// 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 (