Skip to content

Commit f556739

Browse files
authored
feat: Support using structural simplified Ingress resource model in file storage (#34)
1 parent 6f03308 commit f556739

7 files changed

Lines changed: 230 additions & 9 deletions

File tree

compose/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ NACOS_PASSWORD=''
88
NACOS_DATA_ENC_KEY='0123456789abcdef0123456789abcdef'
99
NACOS_SERVER_TAG='v2.2.3'
1010
HIGRESS_RUNNER_TAG='0.0.3'
11-
HIGRESS_API_SERVER_TAG='0.0.8'
11+
HIGRESS_API_SERVER_TAG='0.0.9'
1212
HIGRESS_CONTROLLER_TAG='1.1.1'
1313
HIGRESS_PILOT_TAG='1.1.1'
1414
HIGRESS_GATEWAY_TAG='1.1.1'

compose/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ services:
108108
- --kubeconfig=/home/higress/.kube/config
109109
- --gatewaySelectorKey=higress
110110
- --gatewaySelectorValue=higress-system-higress-gateway
111-
- --ingressClass=higress
111+
- --ingressClass=
112112
depends_on:
113113
apiserver:
114114
condition: service_healthy

src/apiserver/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
REGISTRY ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/
22
IMAGE_NAME ?= api-server
3-
IMAGE_VERSION ?= 0.0.8
3+
IMAGE_VERSION ?= 0.0.9
44
BUILD_TIME := $(shell date "+%Y%m%d-%H%M%S")
55
COMMIT_ID := $(shell git rev-parse --short HEAD 2>/dev/null)
66
IMAGE_TAG = $(if $(strip $(IMAGE_VERSION)),${IMAGE_VERSION},${BUILD_TIME}-${COMMIT_ID})

src/apiserver/pkg/apiserver/apiserver.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package apiserver
1818

1919
import (
2020
"fmt"
21+
"github.com/alibaba/higress/api-server/pkg/codec"
2122
"github.com/alibaba/higress/api-server/pkg/options"
2223
"github.com/alibaba/higress/api-server/pkg/registry"
2324
"github.com/alibaba/higress/api-server/pkg/storage"
@@ -167,7 +168,7 @@ func (c completedConfig) New() (*HigressServer, error) {
167168

168169
storageCreateFunc := func(
169170
groupResource schema.GroupResource,
170-
codec runtime.Codec,
171+
runtimeCodec runtime.Codec,
171172
isNamespaced bool,
172173
singularName string,
173174
newFunc func() runtime.Object,
@@ -177,13 +178,14 @@ func (c completedConfig) New() (*HigressServer, error) {
177178
) (rest.Storage, error) {
178179
switch storageMode {
179180
case options.Storage_File:
180-
return registry.NewFileREST(groupResource, codec, storageOptions.FileOptions.RootDir, extension, isNamespaced, singularName, newFunc, newListFunc, attrFunc)
181+
runtimeCodec = codec.NewFlatAwareCodec(groupResource, runtimeCodec)
182+
return registry.NewFileREST(groupResource, runtimeCodec, storageOptions.FileOptions.RootDir, extension, isNamespaced, singularName, newFunc, newListFunc, attrFunc)
181183
case options.Storage_Nacos:
182184
var encryptionKey []byte = nil
183185
if sensitive {
184186
encryptionKey = storageOptions.NacosOptions.EncryptionKey
185187
}
186-
return registry.NewNacosREST(groupResource, codec, nacosConfigClient, isNamespaced, singularName, newFunc, newListFunc, attrFunc, encryptionKey), nil
188+
return registry.NewNacosREST(groupResource, runtimeCodec, nacosConfigClient, isNamespaced, singularName, newFunc, newListFunc, attrFunc, encryptionKey), nil
187189
default:
188190
panic(fmt.Errorf("invalid storage mode: %s", storageMode))
189191
}
@@ -326,19 +328,20 @@ func appendStorage(storages map[string]rest.Storage,
326328
sensitive bool,
327329
) {
328330
groupResource := groupVersion.WithResource(pluralName).GroupResource()
329-
codec, _, err := genericserverstorage.NewStorageCodec(genericserverstorage.StorageCodecConfig{
331+
storageCodec, _, err := genericserverstorage.NewStorageCodec(genericserverstorage.StorageCodecConfig{
330332
StorageMediaType: contentType,
331333
StorageSerializer: serializer.NewCodecFactory(Scheme),
332334
StorageVersion: Scheme.PrioritizedVersionsForGroup(groupResource.Group)[0],
333335
MemoryVersion: Scheme.PrioritizedVersionsForGroup(groupResource.Group)[0],
334336
Config: storagebackend.Config{}, // useless fields
335337
})
336338
if err != nil {
337-
err = fmt.Errorf("unable to create REST storage for a resource due to %v, will die", err)
339+
err = fmt.Errorf("unable to create storage codec for a resource due to %v, will die", err)
338340
panic(err)
339341
}
340-
storage, err := storageCreatorFunc(groupResource, codec, isNamespaced, singularName, newFunc, newListFunc, attrFunc, sensitive)
342+
storage, err := storageCreatorFunc(groupResource, storageCodec, isNamespaced, singularName, newFunc, newListFunc, attrFunc, sensitive)
341343
if err != nil {
344+
err = fmt.Errorf("unable to create REST storage for a resource due to %v, will die", err)
342345
panic(err)
343346
}
344347
storages[pluralName] = storage
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package codec
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"io"
8+
networkingv1 "k8s.io/api/networking/v1"
9+
"k8s.io/apimachinery/pkg/runtime"
10+
"k8s.io/apimachinery/pkg/runtime/schema"
11+
kjson "sigs.k8s.io/json"
12+
"sigs.k8s.io/yaml"
13+
)
14+
15+
var (
16+
v1IngressGvr = networkingv1.SchemeGroupVersion.WithResource("ingresses")
17+
v1IngressGvk = networkingv1.SchemeGroupVersion.WithKind("Ingress")
18+
v1IngressApiVersion = networkingv1.SchemeGroupVersion.Group + "/" + networkingv1.SchemeGroupVersion.Version
19+
20+
noApiVersionKindError = fmt.Errorf("the given data doesn't contain APIVersion and Kind data")
21+
nonFlatResourceError = fmt.Errorf("the given data doesn't represent a flat resource")
22+
)
23+
24+
func NewFlatAwareCodec(groupResource schema.GroupResource, innerCodec runtime.Codec) runtime.Codec {
25+
if v1IngressGvr.GroupResource() == groupResource {
26+
return &flatIngressCodec{groupResource: groupResource, innerCodec: innerCodec}
27+
}
28+
return innerCodec
29+
}
30+
31+
type flatIngressCodec struct {
32+
groupResource schema.GroupResource
33+
innerCodec runtime.Codec
34+
}
35+
36+
func (c *flatIngressCodec) Encode(obj runtime.Object, w io.Writer) error {
37+
ingress, ok := obj.(*networkingv1.Ingress)
38+
if !ok {
39+
return c.innerCodec.Encode(obj, w)
40+
}
41+
fIngress := toFlatIngress(ingress)
42+
return encodeFlatResource(fIngress, w)
43+
}
44+
45+
func (c *flatIngressCodec) Identifier() runtime.Identifier {
46+
return c.innerCodec.Identifier()
47+
}
48+
49+
func (c *flatIngressCodec) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
50+
fIngress := &flatIngress{}
51+
err := decodeFlatResource(data, fIngress)
52+
if err != nil {
53+
if errors.Is(err, nonFlatResourceError) {
54+
return c.innerCodec.Decode(data, defaults, into)
55+
}
56+
return nil, nil, err
57+
}
58+
ingress := fromFlatIngress(fIngress)
59+
ingress.APIVersion = v1IngressApiVersion
60+
ingress.Kind = v1IngressGvk.Kind
61+
return ingress, defaults, err
62+
}
63+
64+
func encodeFlatResource(obj interface{}, w io.Writer) error {
65+
jsonData, err := json.Marshal(obj)
66+
if err != nil {
67+
return err
68+
}
69+
yamlData, err := yaml.JSONToYAML(jsonData)
70+
if err != nil {
71+
return err
72+
}
73+
_, err = w.Write(yamlData)
74+
return err
75+
}
76+
77+
func decodeFlatResource(data []byte, into interface{}) error {
78+
jsonData, err := yaml.YAMLToJSON(data)
79+
if err != nil {
80+
return err
81+
}
82+
// Compatible with non-flat K8s YAMLs
83+
_, err = tryFindApiVersionKind(jsonData)
84+
if err == nil {
85+
return nonFlatResourceError
86+
}
87+
if err := kjson.UnmarshalCaseSensitivePreserveInts(jsonData, into); err != nil {
88+
return err
89+
}
90+
return nil
91+
}
92+
93+
func tryFindApiVersionKind(data []byte) (*schema.GroupVersionKind, error) {
94+
findKind := struct {
95+
// +optional
96+
APIVersion string `json:"apiVersion,omitempty"`
97+
// +optional
98+
Kind string `json:"kind,omitempty"`
99+
}{}
100+
if err := json.Unmarshal(data, &findKind); err != nil {
101+
return nil, err
102+
}
103+
if findKind.APIVersion == "" || findKind.Kind == "" {
104+
return nil, noApiVersionKindError
105+
}
106+
gv, err := schema.ParseGroupVersion(findKind.APIVersion)
107+
if err != nil {
108+
return nil, err
109+
}
110+
return &schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.Kind}, nil
111+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package codec
2+
3+
import (
4+
networkingv1 "k8s.io/api/networking/v1"
5+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
6+
"k8s.io/apimachinery/pkg/runtime"
7+
"k8s.io/apimachinery/pkg/runtime/schema"
8+
)
9+
10+
type flatObjectMeta struct {
11+
Labels map[string]string `json:"labels,omitempty"`
12+
Annotations map[string]string `json:"annotations,omitempty"`
13+
}
14+
15+
func (in *flatObjectMeta) DeepCopyInto(out *flatObjectMeta) {
16+
if in.Labels != nil {
17+
in, out := &in.Labels, &out.Labels
18+
*out = make(map[string]string, len(*in))
19+
for key, val := range *in {
20+
(*out)[key] = val
21+
}
22+
}
23+
if in.Annotations != nil {
24+
in, out := &in.Annotations, &out.Annotations
25+
*out = make(map[string]string, len(*in))
26+
for key, val := range *in {
27+
(*out)[key] = val
28+
}
29+
}
30+
}
31+
32+
type flatIngress struct {
33+
metav1.TypeMeta `json:"-"`
34+
flatObjectMeta `json:",inline"`
35+
36+
// Spec exploded
37+
DefaultBackend *networkingv1.IngressBackend `json:"defaultBackend,omitempty" protobuf:"bytes,1,opt,name=defaultBackend"`
38+
TLS []networkingv1.IngressTLS `json:"tls,omitempty" protobuf:"bytes,2,rep,name=tls"`
39+
Rules []networkingv1.IngressRule `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"`
40+
}
41+
42+
func (f *flatIngress) GetObjectKind() schema.ObjectKind {
43+
return f.TypeMeta.GetObjectKind()
44+
}
45+
46+
func (f *flatIngress) DeepCopyObject() runtime.Object {
47+
out := flatIngress{}
48+
49+
out.TypeMeta = f.TypeMeta
50+
f.flatObjectMeta.DeepCopyInto(&out.flatObjectMeta)
51+
52+
out.DefaultBackend = f.DefaultBackend
53+
if f.TLS != nil {
54+
in, out := &f.TLS, &out.TLS
55+
*out = make([]networkingv1.IngressTLS, len(*in))
56+
for i := range *in {
57+
(*in)[i].DeepCopyInto(&(*out)[i])
58+
}
59+
}
60+
if f.Rules != nil {
61+
in, out := &f.Rules, &out.Rules
62+
*out = make([]networkingv1.IngressRule, len(*in))
63+
for i := range *in {
64+
(*in)[i].DeepCopyInto(&(*out)[i])
65+
}
66+
}
67+
68+
return nil
69+
}
70+
71+
func toFlatIngress(ingress *networkingv1.Ingress) *flatIngress {
72+
return &flatIngress{
73+
flatObjectMeta: flatObjectMeta{
74+
Labels: ingress.Labels,
75+
Annotations: ingress.Annotations,
76+
},
77+
DefaultBackend: ingress.Spec.DefaultBackend,
78+
TLS: ingress.Spec.TLS,
79+
Rules: ingress.Spec.Rules,
80+
}
81+
}
82+
83+
func fromFlatIngress(flat *flatIngress) *networkingv1.Ingress {
84+
return &networkingv1.Ingress{
85+
ObjectMeta: metav1.ObjectMeta{
86+
Labels: flat.Labels,
87+
Annotations: flat.Annotations,
88+
},
89+
Spec: networkingv1.IngressSpec{
90+
DefaultBackend: flat.DefaultBackend,
91+
TLS: flat.TLS,
92+
Rules: flat.Rules,
93+
},
94+
}
95+
}

src/apiserver/pkg/registry/file_rest.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var ErrFileNotExists = fmt.Errorf("file doesn't exist")
3636

3737
const fileChangeProcessInterval = 100 * time.Millisecond
3838
const fileChangeProcessDelay = 250 * time.Millisecond
39+
const defaultNamespace = "higress-system"
3940

4041
var _ rest.StandardStorage = &fileREST{}
4142
var _ rest.Scoper = &fileREST{}
@@ -496,6 +497,7 @@ func (f *fileREST) objectFileName(ctx context.Context, name string) string {
496497
}
497498

498499
func (f *fileREST) write(encoder runtime.Encoder, filepath string, obj runtime.Object) error {
500+
f.normalizeObjectMeta(obj, filepath)
499501
buf := new(bytes.Buffer)
500502
if err := encoder.Encode(obj, buf); err != nil {
501503
return err
@@ -521,9 +523,19 @@ func (f *fileREST) read(decoder runtime.Decoder, path string, newFunc func() run
521523
if err != nil {
522524
return nil, err
523525
}
526+
f.normalizeObjectMeta(decodedObj, cleanedPath)
524527
return decodedObj, nil
525528
}
526529

530+
func (f *fileREST) normalizeObjectMeta(obj runtime.Object, path string) {
531+
accessor, err := meta.Accessor(obj)
532+
if err != nil {
533+
return
534+
}
535+
accessor.SetNamespace(defaultNamespace)
536+
accessor.SetName(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)))
537+
}
538+
527539
func (f *fileREST) visitDir(dirname string, extension string, newFunc func() runtime.Object, codec runtime.Decoder, visitFunc func(string, runtime.Object)) error {
528540
return filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error {
529541
if err != nil {

0 commit comments

Comments
 (0)