Commit 5c2f0675 authored by kinarashah's avatar kinarashah Committed by Alena Prokharchyk
Browse files

handle user updated metadata settings

metadata settings related to k8s versions current, default and deprecated
shouldn't get overridden once user updates them
parent 1ff86604
Showing with 355 additions and 75 deletions
+355 -75
......@@ -67,14 +67,12 @@ func (a ActionHandler) refresh(apiContext *types.APIContext) error {
response := map[string]interface{}{}
url, err := kd.GetURLSettingValue()
if err != nil {
response["message"] = fmt.Sprintf("failed to get settings %v", err)
apiContext.WriteResponse(http.StatusInternalServerError, response)
return err
msg := fmt.Sprintf("failed to get settings %v", err)
return httperror.WrapAPIError(err, httperror.ServerError, msg)
}
if err := a.MetadataHandler.Refresh(url, false); err != nil {
response["message"] = fmt.Sprintf("failed to refresh %v", err)
apiContext.WriteResponse(http.StatusInternalServerError, response)
return err
msg := fmt.Sprintf("failed to refresh %v", err)
return httperror.WrapAPIError(err, httperror.ServerError, msg)
}
apiContext.WriteResponse(http.StatusOK, response)
return nil
......
......@@ -704,6 +704,8 @@ func KontainerDriver(schemas *types.Schemas, management *config.ScaledContext) {
ServiceOptions: management.Management.RKEK8sServiceOptions(""),
AddonsLister: management.Management.RKEAddons("").Controller().Lister(),
Addons: management.Management.RKEAddons(""),
SettingLister: management.Management.Settings("").Controller().Lister(),
Settings: management.Management.Settings(""),
}
handler := kontainerdriver.ActionHandler{
KontainerDrivers: management.Management.KontainerDrivers(""),
......
......@@ -3,6 +3,7 @@ package setting
import (
"fmt"
"os"
"strings"
"github.com/rancher/norman/httperror"
"github.com/rancher/norman/store/transform"
......@@ -17,6 +18,16 @@ type Store struct {
types.Store
}
const (
UserUpdateLabel = "io.cattle.user.updated"
)
var MetadataSettings = map[string]bool{
settings.KubernetesVersion.Name: true,
settings.KubernetesVersionsCurrent.Name: true,
settings.KubernetesVersionsDeprecated.Name: true,
}
func New(store types.Store) types.Store {
return &Store{
&transform.Store{
......@@ -52,3 +63,45 @@ func (s *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id st
return s.Store.Delete(apiContext, schema, id)
}
func (s *Store) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
if _, ok := MetadataSettings[id]; ok {
labels := map[string]interface{}{}
if val, ok := data["labels"]; ok {
labels = convert.ToMapInterface(val)
}
if val, ok := data["value"]; ok && convert.ToString(val) == "" {
labels[UserUpdateLabel] = "false"
} else {
if id == settings.KubernetesVersion.Name || id == settings.KubernetesVersionsCurrent.Name {
if err := validate(id, convert.ToString(val)); err != nil {
return nil, err
}
}
labels[UserUpdateLabel] = "true"
}
data["labels"] = labels
}
return s.Store.Update(apiContext, schema, data, id)
}
func validate(id, value string) error {
var k8sVersion string
var k8sCurrVersions []string
if id == settings.KubernetesVersion.Name {
k8sVersion = value
k8sCurrVersions = strings.Split(settings.KubernetesVersionsCurrent.Get(), ",")
} else {
k8sCurrVersions = strings.Split(value, ",")
k8sVersion = settings.KubernetesVersion.Get()
}
for _, curr := range k8sCurrVersions {
if curr == k8sVersion {
return nil
}
}
return httperror.NewAPIError(httperror.MissingRequired, "default k8s-version must be present in k8s-versions-current")
}
......@@ -8,21 +8,17 @@ import (
"strings"
"github.com/blang/semver"
"github.com/rancher/kontainer-driver-metadata/rke/templates"
"github.com/sirupsen/logrus"
"github.com/rancher/kontainer-driver-metadata/rke"
mVersion "github.com/mcuadros/go-version"
"github.com/rancher/kontainer-driver-metadata/rke"
"github.com/rancher/kontainer-driver-metadata/rke/templates"
"github.com/rancher/norman/types/convert"
setting2 "github.com/rancher/rancher/pkg/api/store/setting"
"github.com/rancher/rancher/pkg/namespace"
"github.com/rancher/rancher/pkg/settings"
"github.com/rancher/rke/util"
"github.com/rancher/rancher/pkg/namespace"
v3 "github.com/rancher/types/apis/management.cattle.io/v3"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
......@@ -46,6 +42,23 @@ const (
var existLabel = map[string]string{sendRKELabel: "false"}
//settings corresponding to keys in setting2.MetadataSettings
var userUpdateSettingMap = map[string]settings.Setting{
settings.KubernetesVersion.Name: settings.KubernetesVersion,
settings.KubernetesVersionsCurrent.Name: settings.KubernetesVersionsCurrent,
settings.KubernetesVersionsDeprecated.Name: settings.KubernetesVersionsDeprecated,
}
var rancherUpdateSettingMap = map[string]settings.Setting{
settings.KubernetesVersion.Name: settings.KubernetesVersion,
settings.KubernetesVersionsCurrent.Name: settings.KubernetesVersionsCurrent,
settings.KubernetesVersionsDeprecated.Name: settings.KubernetesVersionsDeprecated,
settings.UIKubernetesDefaultVersion.Name: settings.UIKubernetesDefaultVersion,
settings.UIKubernetesSupportedVersions.Name: settings.UIKubernetesSupportedVersions,
settings.KubernetesVersionToSystemImages.Name: settings.KubernetesVersionToSystemImages,
settings.KubernetesVersionToServiceOptions.Name: settings.KubernetesVersionToServiceOptions,
}
func (md *MetadataController) createOrUpdateMetadata(data Data) error {
if err := md.saveSystemImages(data.K8sVersionRKESystemImages, data.K8sVersionedTemplates,
data.K8sVersionInfo, data.K8sVersionServiceOptions, data.K8sVersionWindowsServiceOptions, data.RancherDefaultK8sVersions); err != nil {
......@@ -116,7 +129,8 @@ func (md *MetadataController) saveSystemImages(K8sVersionRKESystemImages map[str
}
}
logrus.Debugf("driverMetadata deprecated %v max incompatible versions %v", deprecatedMap, maxIgnore)
return updateSettings(maxVersionForMajorK8sVersion, rancherVersion, ServiceOptions, DefaultK8sVersions, deprecatedMap)
return md.updateSettings(maxVersionForMajorK8sVersion, rancherVersion, ServiceOptions, DefaultK8sVersions, deprecatedMap)
}
func toIgnoreForAllK8s(rancherVersionInfo v3.K8sVersionInfo, rancherVersion string) bool {
......@@ -387,63 +401,250 @@ func getWindowsName(str string) string {
return fmt.Sprintf("w%s", str)
}
func updateSettings(maxVersionForMajorK8sVersion map[string]string, rancherVersion string,
K8sVersionServiceOptions map[string]v3.KubernetesServicesOptions, DefaultK8sVersions map[string]string, deprecated map[string]bool) error {
func (md *MetadataController) updateSettings(maxVersionForMajorK8sVersion map[string]string, rancherVersion string,
K8sVersionServiceOptions map[string]v3.KubernetesServicesOptions, DefaultK8sVersions map[string]string,
deprecated map[string]bool) error {
userSettings, userUpdated, err := md.getUserSettings()
if err != nil {
return err
}
updateSettings, err := toUpdate(maxVersionForMajorK8sVersion, deprecated, DefaultK8sVersions, rancherVersion, K8sVersionServiceOptions)
if err != nil {
return err
}
if err := md.updateSettingDefaultFromFields(updateSettings); err != nil {
return err
}
if !userUpdated {
if err := md.updateSettingFromFields(updateSettings, map[string]string{}); err != nil {
return err
}
} else {
userMaxVersionForMajorK8sVersion, userDeprecated, err := getUserSettings(userSettings, DefaultK8sVersions)
if err != nil {
return err
}
if len(userMaxVersionForMajorK8sVersion) == 0 {
userMaxVersionForMajorK8sVersion = maxVersionForMajorK8sVersion
}
if len(userDeprecated) == 0 {
userDeprecated = deprecated
}
userUpdateSettings, err := toUpdate(userMaxVersionForMajorK8sVersion, userDeprecated, DefaultK8sVersions, rancherVersion, K8sVersionServiceOptions)
if err != nil {
return err
}
if err := md.updateSettingFromFields(userUpdateSettings, userSettings); err != nil {
return err
}
}
return nil
}
func (md *MetadataController) getUserSettings() (map[string]string, bool, error) {
userSettings := map[string]string{}
get := func(key string) string {
if setting, ok := userUpdateSettingMap[key]; ok {
return setting.Get()
}
return ""
}
for key := range userUpdateSettingMap {
setting, err := md.SettingLister.Get("", key)
if err != nil {
if !errors.IsNotFound(err) {
return nil, false, fmt.Errorf("driverMetadata: error getting setting %s: %v", key, err)
}
setting, err = md.Settings.Get(key, metav1.GetOptions{})
if err != nil {
return nil, false, fmt.Errorf("driverMetadata: error getting setting %s: %v", key, err)
}
}
if val, ok := setting.Labels[setting2.UserUpdateLabel]; ok && convert.ToString(val) == "true" {
userSettings[key] = get(key)
}
}
logrus.Debugf("driverMetadata: userSettings %v", userSettings)
if len(userSettings) > 0 {
return userSettings, true, nil
}
return userSettings, false, nil
}
func toUpdate(maxVersionForMajorK8sVersion map[string]string, deprecated map[string]bool,
defaultK8sVersions map[string]string, rancherVersion string, k8sVersionServiceOptions map[string]v3.KubernetesServicesOptions) (map[string]string, error) {
var k8sVersionsCurrent []string
var maxVersions []string
for k, v := range maxVersionForMajorK8sVersion {
if !deprecated[k] {
k8sVersionsCurrent = append(k8sVersionsCurrent, v)
maxVersions = append(maxVersions, k)
}
}
if len(maxVersions) == 0 {
return nil, fmt.Errorf("driverMetadata: no max version %v", maxVersionForMajorK8sVersion)
}
sort.Strings(k8sVersionsCurrent)
sort.Strings(maxVersions)
defaultK8sVersion, err := getDefaultK8sVersion(defaultK8sVersions, k8sVersionsCurrent, rancherVersion)
if err != nil {
return nil, err
}
k8sVersionRKESystemImages := map[string]interface{}{}
k8sVersionSvcOptions := map[string]v3.KubernetesServicesOptions{}
for majorVersion, k8sVersion := range maxVersionForMajorK8sVersion {
if !deprecated[k8sVersion] {
k8sVersionRKESystemImages[k8sVersion] = nil
k8sVersionSvcOptions[k8sVersion] = K8sVersionServiceOptions[majorVersion]
k8sVersionSvcOptions[k8sVersion] = k8sVersionServiceOptions[majorVersion]
}
}
var keys []string
for k := range maxVersionForMajorK8sVersion {
if !deprecated[k] {
keys = append(keys, k)
}
k8sCurrRKEdata, err := marshal(k8sVersionRKESystemImages)
if err != nil {
return nil, err
}
sort.Strings(keys)
return SaveSettings(k8sVersionRKESystemImages, k8sVersionSvcOptions, DefaultK8sVersions, rancherVersion, keys, deprecated)
}
func SaveSettings(k8sCurrVersions map[string]interface{},
k8sVersionSvcOptions map[string]v3.KubernetesServicesOptions, rancherDefaultK8sVersions map[string]string,
rancherVersion string, maxVersions []string, deprecated map[string]bool) error {
k8sSvcOptionData, err := marshal(k8sVersionSvcOptions)
if err != nil {
return nil, err
}
k8sCurrVersionData, err := marshal(k8sCurrVersions)
deprecatedData, err := marshal(deprecated)
if err != nil {
return err
return nil, err
}
minVersion := maxVersions[0]
maxVersion := util.GetTagMajorVersion(defaultK8sVersion)
uiSupported := fmt.Sprintf(">=%s.x <=%s.x", minVersion, maxVersion)
uiDefaultRange := fmt.Sprintf("<=%s.x", maxVersion)
return map[string]string{
settings.KubernetesVersionsCurrent.Name: strings.Join(k8sVersionsCurrent, ","),
settings.KubernetesVersion.Name: defaultK8sVersion,
settings.KubernetesVersionsDeprecated.Name: deprecatedData,
settings.UIKubernetesDefaultVersion.Name: uiDefaultRange,
settings.UIKubernetesSupportedVersions.Name: uiSupported,
settings.KubernetesVersionToSystemImages.Name: k8sCurrRKEdata,
settings.KubernetesVersionToServiceOptions.Name: k8sSvcOptionData,
}, nil
}
func (md *MetadataController) updateSettingFromFields(updateField map[string]string, skip map[string]string) error {
for key, setting := range rancherUpdateSettingMap {
if _, ok := skip[key]; ok {
continue
}
if _, ok := updateField[key]; !ok {
return fmt.Errorf("driverMetadata: updated value not present for setting %s", key)
}
oldVal := setting.Get()
newVal := updateField[key]
if oldVal != newVal {
if err := setting.Set(newVal); err != nil {
return err
}
}
}
var versions []string
for k := range k8sCurrVersions {
versions = append(versions, k)
return nil
}
func (md *MetadataController) updateSettingDefaultFromFields(updateField map[string]string) error {
for key := range rancherUpdateSettingMap {
val, ok := updateField[key]
if !ok {
return fmt.Errorf("driverMetadata: default value not present for setting %s", key)
}
setting, err := md.SettingLister.Get("", key)
if err != nil {
if !errors.IsNotFound(err) {
return err
}
setting, err = md.Settings.Get(key, metav1.GetOptions{})
if err != nil {
return err
}
}
if setting.Default == val {
continue
}
settingCopy := setting.DeepCopy()
settingCopy.Default = val
_, err = md.Settings.Update(settingCopy)
if err != nil {
return err
}
}
sort.Strings(versions)
if err := settings.KubernetesVersionToSystemImages.Set(k8sCurrVersionData); err != nil {
return err
return nil
}
func getUserSettings(userSettings map[string]string, defaultK8sVersions map[string]string) (map[string]string, map[string]bool, error) {
userMaxVersionForMajorK8sVersion := map[string]string{}
if val, ok := userSettings[settings.KubernetesVersionsCurrent.Name]; ok {
versions := strings.Split(val, ",")
for _, version := range versions {
userMaxVersionForMajorK8sVersion[util.GetTagMajorVersion(version)] = version
}
}
if err := settings.KubernetesVersionsCurrent.Set(strings.Join(versions, ",")); err != nil {
return err
userDeprecated := map[string]bool{}
if val, ok := userSettings[settings.KubernetesVersionsDeprecated.Name]; ok {
deprecatedVersions := make(map[string]bool)
if val != "" {
if err := json.Unmarshal([]byte(val), &deprecatedVersions); err != nil {
return nil, nil, err
}
}
for key, val := range deprecatedVersions {
userDeprecated[key] = val
}
}
k8sSvcOptionData, err := marshal(k8sVersionSvcOptions)
if err != nil {
return err
if val, ok := userSettings[settings.KubernetesVersion.Name]; ok {
defaultK8sVersions["user"] = val
}
if err := settings.KubernetesVersionToServiceOptions.Set(k8sSvcOptionData); err != nil {
return err
return userMaxVersionForMajorK8sVersion, userDeprecated, nil
}
func getDefaultK8sVersion(rancherDefaultK8sVersions map[string]string, k8sCurrVersions []string, rancherVersion string) (string, error) {
defaultK8sVersion, ok := rancherDefaultK8sVersions["user"]
if defaultK8sVersion != "" {
found := false
for _, k8sVersion := range k8sCurrVersions {
if k8sVersion == defaultK8sVersion {
found = true
break
}
}
if !found {
return "", fmt.Errorf("driverMetadata: unable to find default k8s version in current k8s %s %v", defaultK8sVersion, k8sCurrVersions)
}
return defaultK8sVersion, nil
}
defaultK8sVersionRange, ok := rancherDefaultK8sVersions[rancherVersion]
if !ok || defaultK8sVersionRange == "" {
defaultK8sVersionRange = rancherDefaultK8sVersions["default"]
}
// get matching default k8s from k8s curr
toMatch := util.GetTagMajorVersion(defaultK8sVersionRange)
defaultK8sVersion := ""
for k8sCurr := range k8sCurrVersions {
for _, k8sCurr := range k8sCurrVersions {
toTest := util.GetTagMajorVersion(k8sCurr)
if toTest == toMatch {
defaultK8sVersion = k8sCurr
......@@ -451,33 +652,9 @@ func SaveSettings(k8sCurrVersions map[string]interface{},
}
}
if defaultK8sVersion == "" {
return fmt.Errorf("unable to find default k8s version in current k8s %s %v", defaultK8sVersionRange, versions)
}
if err := settings.KubernetesVersion.Set(defaultK8sVersion); err != nil {
return err
}
if len(maxVersions) > 0 {
minVersion := maxVersions[0]
maxVersion := util.GetTagMajorVersion(defaultK8sVersion)
uiSupported := fmt.Sprintf(">=%s.x <=%s.x", minVersion, maxVersion)
uiDefaultRange := fmt.Sprintf("<=%s.x", maxVersion)
if err := settings.UIKubernetesSupportedVersions.Set(uiSupported); err != nil {
return err
}
if err := settings.UIKubernetesDefaultVersion.Set(uiDefaultRange); err != nil {
return err
}
return "", fmt.Errorf("driverMetadata: unable to find default k8s version in current k8s %s %v", defaultK8sVersionRange, k8sCurrVersions)
}
deprecatedData, err := marshal(deprecated)
if err != nil {
return err
}
if err := settings.KubernetesVersionsDeprecated.Set(deprecatedData); err != nil {
return err
}
return nil
return defaultK8sVersion, nil
}
func marshal(data interface{}) (string, error) {
......
......@@ -30,6 +30,8 @@ type MetadataController struct {
ServiceOptions v3.RKEK8sServiceOptionInterface
AddonsLister v3.RKEAddonLister
Addons v3.RKEAddonInterface
SettingLister v3.SettingLister
Settings v3.SettingInterface
ctx context.Context
}
......@@ -86,6 +88,8 @@ func Register(ctx context.Context, management *config.ManagementContext) {
ServiceOptions: mgmt.RKEK8sServiceOptions(""),
AddonsLister: mgmt.RKEAddons("").Controller().Lister(),
Addons: mgmt.RKEAddons(""),
SettingLister: mgmt.Settings("").Controller().Lister(),
Settings: mgmt.Settings(""),
ctx: ctx,
}
......
......@@ -331,3 +331,49 @@ def verify_driver_not_in_types(client, kd):
wait_until(check)
client.reload_schema()
assert kd.name + 'EngineConfig' not in client.schema.types
@pytest.mark.nonparallel
def test_user_update_settings(admin_mc):
client = admin_mc.client
k8s_version_setting = client.by_id_setting('k8s-version')
default_k8s_version = k8s_version_setting["default"]
k8s_versions_curr = client.by_id_setting(
'k8s-versions-current')["value"].split(",")
# user updates correct value
user_value = k8s_versions_curr[0]
updated_version = admin_mc.client.update_by_id_setting(
id='k8s-version', value=user_value)
assert updated_version["default"] == default_k8s_version
assert updated_version["value"] == user_value
assert updated_version["labels"]["io.cattle.user.updated"] == "true"
# assert refresh action doesn't override
lister = client.list_kontainerDriver()
try:
client.action(obj=lister, action_name="refresh")
except ApiError as e:
assert e.value.error.status == 422
new_k8s_version = client.by_id_setting('k8s-version')
assert new_k8s_version["default"] == default_k8s_version
assert new_k8s_version["value"] == user_value
# user updates invalid value
user_value = "v1.15.4-rancher13"
try:
updated_version = admin_mc.client.update_by_id_setting(
id='k8s-version', value=user_value)
except ApiError as e:
assert e.error.code == "MissingRequired"
assert e.error.status == 422
# bring back the default value, user updates with empty value
user_value = ""
updated_version = admin_mc.client.update_by_id_setting(
id='k8s-version', value=user_value)
assert updated_version["default"] == default_k8s_version
assert updated_version["value"] == default_k8s_version
assert updated_version["labels"]["io.cattle.user.updated"] == "false"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment