1
0
mirror of https://github.com/rancher/types.git synced 2025-04-27 18:25:05 +00:00

Add HPA type to schema

This commit is contained in:
orangedeng 2019-03-28 21:16:41 +08:00 committed by Craig Jellick
parent 02110b7332
commit 5694548201
7 changed files with 317 additions and 1 deletions

View File

@ -10,6 +10,7 @@ import (
"github.com/rancher/types/factory"
"github.com/rancher/types/mapper"
"k8s.io/api/apps/v1beta2"
autoscaling "k8s.io/api/autoscaling/v2beta2"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
"k8s.io/api/core/v1"
@ -44,7 +45,8 @@ var (
Init(workloadTypes).
Init(appTypes).
Init(pipelineTypes).
Init(monitoringTypes)
Init(monitoringTypes).
Init(autoscalingTypes)
)
func configMapTypes(schemas *types.Schemas) *types.Schemas {
@ -1020,3 +1022,69 @@ func monitoringTypes(schemas *types.Schemas) *types.Schemas {
).
MustImport(&Version, monitoringv1.Alertmanager{}, projectOverride{})
}
func autoscalingTypes(schemas *types.Schemas) *types.Schemas {
return schemas.
AddMapperForType(&Version, autoscaling.HorizontalPodAutoscaler{},
&m.ChangeType{Field: "scaleTargetRef", Type: "reference[workload]"},
&m.Move{From: "scaleTargetRef", To: "workloadId"},
mapper.CrossVersionObjectToWorkload{Field: "workloadId"},
&m.Required{Fields: []string{"workloadId", "maxReplicas"}},
&m.AnnotationField{Field: "displayName"},
&m.DisplayName{},
&m.AnnotationField{Field: "description"},
&m.Embed{Field: "status"},
mapper.NewMergeListByIndexMapper("currentMetrics", "metrics", "type"),
).
AddMapperForType(&Version, autoscaling.MetricTarget{},
&m.Enum{Field: "type", Options: []string{"Utilization", "Value", "AverageValue"}},
&m.Move{To: "utilization", From: "averageUtilization"},
).
AddMapperForType(&Version, autoscaling.MetricValueStatus{},
&m.Move{To: "utilization", From: "averageUtilization"},
).
AddMapperForType(&Version, autoscaling.MetricSpec{},
&m.Condition{Field: "type", Value: "Object", Mapper: types.Mappers{
&m.Move{To: "target", From: "object/target", DestDefined: true, NoDeleteFromField: true},
&m.Move{To: "metric", From: "object/metric", DestDefined: true, NoDeleteFromField: true},
}},
&m.Condition{Field: "type", Value: "Pods", Mapper: types.Mappers{
&m.Move{To: "target", From: "pods/target", DestDefined: true, NoDeleteFromField: true},
&m.Move{To: "metric", From: "pods/metric", DestDefined: true, NoDeleteFromField: true},
}},
&m.Condition{Field: "type", Value: "Resource", Mapper: types.Mappers{
&m.Move{To: "metric/name", From: "resource/name", DestDefined: true, NoDeleteFromField: true},
&m.Move{To: "target", From: "resource/target", DestDefined: true, NoDeleteFromField: true},
}},
&m.Condition{Field: "type", Value: "External", Mapper: types.Mappers{
&m.Move{To: "target", From: "external/target", DestDefined: true, NoDeleteFromField: true},
&m.Move{To: "metric", From: "external/metric", DestDefined: true, NoDeleteFromField: true},
}},
&m.Embed{Field: "object", Ignore: []string{"target", "metric"}},
&m.Embed{Field: "pods", Ignore: []string{"target", "metric"}},
&m.Embed{Field: "external", Ignore: []string{"target", "metric"}},
&m.Embed{Field: "resource", Ignore: []string{"target", "name"}},
&m.Embed{Field: "metric"},
&m.Enum{Field: "type", Options: []string{"Object", "Pods", "Resource", "External"}},
).
MustImportAndCustomize(&Version, autoscaling.MetricSpec{}, func(s *types.Schema) {
s.CodeName = "Metric"
s.PluralName = "metrics"
s.ID = "metric"
s.CodeNamePlural = "Metrics"
}, struct {
Target autoscaling.MetricTarget `json:"target"`
Metric autoscaling.MetricIdentifier `json:"metric"`
Current autoscaling.MetricValueStatus `json:"current" norman:"nocreate,noupdate"`
}{}).
AddMapperForType(&Version, autoscaling.MetricStatus{},
&m.Condition{Field: "type", Value: "Object", Mapper: &m.Move{To: "current", From: "object/current", DestDefined: true, NoDeleteFromField: true}},
&m.Condition{Field: "type", Value: "Pods", Mapper: &m.Move{To: "current", From: "pods/current", DestDefined: true, NoDeleteFromField: true}},
&m.Condition{Field: "type", Value: "Resource", Mapper: &m.Move{To: "current", From: "resource/current"}},
&m.Condition{Field: "type", Value: "External", Mapper: &m.Move{To: "current", From: "external/current", DestDefined: true, NoDeleteFromField: true}},
).
MustImport(&Version, autoscaling.HorizontalPodAutoscaler{}, projectOverride{}, struct {
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
}{})
}

View File

@ -10,6 +10,7 @@ import (
"github.com/rancher/norman/store/proxy"
"github.com/rancher/norman/types"
appsv1beta2 "github.com/rancher/types/apis/apps/v1beta2"
autoscaling "github.com/rancher/types/apis/autoscaling/v2beta2"
batchv1 "github.com/rancher/types/apis/batch/v1"
batchv1beta1 "github.com/rancher/types/apis/batch/v1beta1"
clusterv3 "github.com/rancher/types/apis/cluster.cattle.io/v3"
@ -178,6 +179,7 @@ type UserContext struct {
K8sClient kubernetes.Interface
Apps appsv1beta2.Interface
Autoscaling autoscaling.Interface
Project projectv3.Interface
Core corev1.Interface
RBAC rbacv1.Interface
@ -212,6 +214,7 @@ func (w *UserContext) UserOnlyContext() *UserOnlyContext {
UnversionedClient: w.UnversionedClient,
K8sClient: w.K8sClient,
Autoscaling: w.Autoscaling,
Apps: w.Apps,
Project: w.Project,
Core: w.Core,
@ -232,6 +235,7 @@ type UserOnlyContext struct {
K8sClient kubernetes.Interface
Apps appsv1beta2.Interface
Autoscaling autoscaling.Interface
Project projectv3.Interface
Core corev1.Interface
RBAC rbacv1.Interface
@ -393,6 +397,11 @@ func NewUserContext(scaledContext *ScaledContext, config rest.Config, clusterNam
return nil, err
}
context.Autoscaling, err = autoscaling.NewForConfig(config)
if err != nil {
return nil, err
}
context.Monitoring, err = monitoringv1.NewForConfig(config)
context.Cluster, err = clusterv3.NewForConfig(config)
if err != nil {
@ -477,6 +486,11 @@ func NewUserOnlyContext(config rest.Config) (*UserOnlyContext, error) {
return nil, err
}
context.Autoscaling, err = autoscaling.NewForConfig(config)
if err != nil {
return nil, err
}
context.Monitoring, err = monitoringv1.NewForConfig(config)
context.Cluster, err = clusterv3.NewForConfig(config)
if err != nil {

View File

@ -14,6 +14,7 @@ import (
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
scalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
"k8s.io/api/core/v1"
extv1beta1 "k8s.io/api/extensions/v1beta1"
knetworkingv1 "k8s.io/api/networking/v1"
@ -89,4 +90,10 @@ func main() {
},
[]interface{}{},
)
generator.GenerateNativeTypes(scalingv2beta2.SchemeGroupVersion,
[]interface{}{
scalingv2beta2.HorizontalPodAutoscaler{},
},
[]interface{}{},
)
}

View File

@ -0,0 +1,76 @@
package mapper
import (
"fmt"
"strings"
"github.com/rancher/norman/types/values"
"github.com/rancher/norman/types"
"github.com/rancher/norman/types/convert"
)
var (
kindMap = map[string]string{
"deployment": "Deployment",
"replicationcontroller": "ReplicationController",
"statefulset": "StatefulSet",
"daemonset": "DaemonSet",
"job": "Job",
"cronjob": "CronJob",
"replicaset": "ReplicaSet",
}
groupVersionMap = map[string]string{
"deployment": "apps/v1beta2",
"replicationcontroller": "core/v1",
"statefulset": "apps/v1beta2",
"daemonset": "apps/v1beta2",
"job": "batch/v1",
"cronjob": "batch/v1beta1",
"replicaset": "apps/v1beta2",
}
)
type CrossVersionObjectToWorkload struct {
Field string
}
func (c CrossVersionObjectToWorkload) ToInternal(data map[string]interface{}) error {
obj, ok := values.GetValue(data, strings.Split(c.Field, "/")...)
if !ok {
return nil
}
workloadID := convert.ToString(obj)
parts := strings.SplitN(workloadID, ":", 3)
newObj := map[string]interface{}{
"kind": getKind(parts[0]),
"apiVersion": groupVersionMap[parts[0]],
"name": parts[2],
}
values.PutValue(data, newObj, strings.Split(c.Field, "/")...)
return nil
}
func (c CrossVersionObjectToWorkload) FromInternal(data map[string]interface{}) {
obj, ok := values.GetValue(data, strings.Split(c.Field, "/")...)
if !ok {
return
}
cvo := convert.ToMapInterface(obj)
ns := convert.ToString(data["namespaceId"])
values.PutValue(data,
fmt.Sprintf("%s:%s:%s",
strings.ToLower(convert.ToString(cvo["kind"])),
ns,
convert.ToString(cvo["name"])),
strings.Split(c.Field, "/")...,
)
}
func (c CrossVersionObjectToWorkload) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
return nil
}
func getKind(i string) string {
return kindMap[i]
}

View File

@ -0,0 +1,111 @@
package mapper
import (
"fmt"
"github.com/rancher/norman/types"
"github.com/rancher/norman/types/convert"
"github.com/rancher/norman/types/definition"
"github.com/rancher/norman/types/mapper"
)
func NewMergeListByIndexMapper(From, To string, Ignores ...string) *MergeListByIndexMapper {
rtn := MergeListByIndexMapper{
From: From,
To: To,
Ignore: make(map[string]struct{}),
fromFields: []string{},
}
for _, Ignore := range Ignores {
rtn.Ignore[Ignore] = struct{}{}
}
return &rtn
}
type MergeListByIndexMapper struct {
From string
To string
Ignore map[string]struct{}
fromFields []string
}
func (m *MergeListByIndexMapper) FromInternal(data map[string]interface{}) {
fromObj, ok := data[m.From]
if !ok {
return
}
toObj, ok := data[m.To]
if !ok {
return
}
fromList := convert.ToMapSlice(fromObj)
toList := convert.ToMapSlice(toObj)
for i := 0; i < len(fromList) && i < len(toList); i++ {
fromItem := fromList[i]
toItem := toList[i]
for key, value := range fromItem {
if _, ignore := m.Ignore[key]; ignore {
continue
}
toItem[key] = value
}
}
delete(data, m.From)
}
func (m *MergeListByIndexMapper) ToInternal(data map[string]interface{}) error {
toObj, ok := data[m.To]
if !ok {
return nil
}
if _, ok = data[m.From]; ok {
return fmt.Errorf("field %s should not exist", m.From)
}
toList := convert.ToMapSlice(toObj)
var fromList []map[string]interface{}
for _, toItem := range toList {
obj := make(map[string]interface{})
for _, field := range m.fromFields {
value, ok := toItem[field]
if !ok {
continue
}
obj[field] = value
if _, ok := m.Ignore[field]; !ok {
delete(toItem, field)
}
}
fromList = append(fromList, obj)
}
data[m.From] = fromList
return nil
}
func (m *MergeListByIndexMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
if err := mapper.ValidateField(m.From, schema); err != nil {
return err
}
fromType := schema.ResourceFields[m.From].Type
if !definition.IsArrayType(fromType) {
return fmt.Errorf("type of field %s in schema %s is not array", m.From, schema.CodeName)
}
fromSchema := schemas.Schema(&schema.Version, definition.SubType(fromType))
for field := range fromSchema.ResourceFields {
m.fromFields = append(m.fromFields, field)
}
if err := mapper.ValidateField(m.To, schema); err != nil {
return err
}
toType := schema.ResourceFields[m.To].Type
if !definition.IsArrayType(toType) {
return fmt.Errorf("type of field %s in schema %s is not array", m.To, schema.CodeName)
}
delete(schema.ResourceFields, m.From)
return nil
}

View File

@ -0,0 +1,38 @@
package mapper
import (
"reflect"
"testing"
)
var (
metrics = []map[string]interface{}{
{"type": "Resource", "resource": "abc"},
{"type": "Object", "object": "def"},
}
currentMetrics = []map[string]interface{}{
{"type": "Resource", "currentResource": "tuvw"},
{"type": "Object", "currentObject": "xyz"},
}
origin = map[string]interface{}{
"metrics": metrics,
"currentMetrics": currentMetrics,
}
)
func Test_MergeList(t *testing.T) {
mapper := NewMergeListByIndexMapper("currentMetrics", "metrics", "type")
mapper.fromFields = []string{"type", "currentResource", "currentObject"}
internal := map[string]interface{}{
"metrics": metrics,
"currentMetrics": currentMetrics,
}
mapper.FromInternal(internal)
if err := mapper.ToInternal(internal); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(internal, origin) {
t.Fatal("merge list not match after parse")
}
}

View File

@ -59,6 +59,8 @@ var transitioningMap = map[string]string{
"Updating": "updating",
"Waiting": "waiting",
"InitialRolesPopulated": "activating",
"ScalingActive": "pending",
"AbleToScale": "pending",
}
// True == error