mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
upgrade to istio 1.19 (#1211)
Co-authored-by: CH3CHO <ch3cho@qq.com> Co-authored-by: rinfx <893383980@qq.com>
This commit is contained in:
257
hgctl/pkg/kubernetes/client.go
Normal file
257
hgctl/pkg/kubernetes/client.go
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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 kubernetes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kubescheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/client-go/util/retry"
|
||||
ctrClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type CLIClient interface {
|
||||
// RESTConfig returns the Kubernetes rest.Config used to configure the clients.
|
||||
RESTConfig() *rest.Config
|
||||
|
||||
// Pod returns the pod for the given namespaced name.
|
||||
Pod(namespacedName types.NamespacedName) (*corev1.Pod, error)
|
||||
|
||||
// PodsForSelector finds pods matching selector.
|
||||
PodsForSelector(namespace string, labelSelectors ...string) (*corev1.PodList, error)
|
||||
|
||||
// PodExec takes a command and the pod data to run the command in the specified pod.
|
||||
PodExec(namespacedName types.NamespacedName, container string, command string) (stdout string, stderr string, err error)
|
||||
|
||||
// ApplyObject creates or updates unstructured object
|
||||
ApplyObject(obj *unstructured.Unstructured) error
|
||||
|
||||
// DeleteObject delete unstructured object
|
||||
DeleteObject(obj *unstructured.Unstructured) error
|
||||
|
||||
// CreateNamespace create namespace
|
||||
CreateNamespace(namespace string) error
|
||||
|
||||
// KubernetesInterface get kubernetes interface
|
||||
KubernetesInterface() kubernetes.Interface
|
||||
}
|
||||
|
||||
var _ CLIClient = &client{}
|
||||
|
||||
type client struct {
|
||||
config *rest.Config
|
||||
restClient *rest.RESTClient
|
||||
kube kubernetes.Interface
|
||||
ctrClient ctrClient.Client
|
||||
}
|
||||
|
||||
func NewCLIClient(clientConfig clientcmd.ClientConfig) (CLIClient, error) {
|
||||
return newClientInternal(clientConfig)
|
||||
}
|
||||
|
||||
func newClientInternal(clientConfig clientcmd.ClientConfig) (*client, error) {
|
||||
var (
|
||||
c client
|
||||
err error
|
||||
)
|
||||
|
||||
c.config, err = clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setRestDefaults(c.config)
|
||||
|
||||
c.restClient, err = rest.RESTClientFor(c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.kube, err = kubernetes.NewForConfig(c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.ctrClient, err = ctrClient.New(c.config, ctrClient.Options{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &c, err
|
||||
}
|
||||
|
||||
func (c *client) RESTConfig() *rest.Config {
|
||||
if c.config == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := *c.config
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (c *client) PodsForSelector(namespace string, podSelectors ...string) (*corev1.PodList, error) {
|
||||
return c.kube.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: strings.Join(podSelectors, ","),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *client) Pod(namespacedName types.NamespacedName) (*corev1.Pod, error) {
|
||||
return c.kube.CoreV1().Pods(namespacedName.Namespace).Get(context.TODO(), namespacedName.Name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (c *client) PodExec(namespacedName types.NamespacedName, container string, command string) (stdout string, stderr string, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if len(stderr) > 0 {
|
||||
err = fmt.Errorf("error exec into %s/%s container %s: %v\n%s",
|
||||
namespacedName.Namespace, namespacedName.Name, container, err, stderr)
|
||||
} else {
|
||||
err = fmt.Errorf("error exec into %s/%s container %s: %v",
|
||||
namespacedName.Namespace, namespacedName.Name, container, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
req := c.restClient.Post().
|
||||
Resource("pods").
|
||||
Namespace(namespacedName.Namespace).
|
||||
Name(namespacedName.Name).
|
||||
SubResource("exec").
|
||||
Param("container", container).
|
||||
VersionedParams(&corev1.PodExecOptions{
|
||||
Container: container,
|
||||
Command: strings.Fields(command),
|
||||
Stdin: false,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
TTY: false,
|
||||
}, kubescheme.ParameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(c.config, "POST", req.URL())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
err = exec.Stream(remotecommand.StreamOptions{
|
||||
Stdin: nil,
|
||||
Stdout: &stdoutBuf,
|
||||
Stderr: &stderrBuf,
|
||||
Tty: false,
|
||||
})
|
||||
|
||||
stdout = stdoutBuf.String()
|
||||
stderr = stderrBuf.String()
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteObject delete unstructured object
|
||||
func (c *client) DeleteObject(obj *unstructured.Unstructured) error {
|
||||
err := c.ctrClient.Delete(context.TODO(), obj, ctrClient.PropagationPolicy(metav1.DeletePropagationBackground))
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyObject creates or updates unstructured object
|
||||
func (c *client) ApplyObject(obj *unstructured.Unstructured) error {
|
||||
if obj.GetKind() == "List" {
|
||||
objList, err := obj.ToList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range objList.Items {
|
||||
if err := c.ApplyObject(&item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
key := ctrClient.ObjectKeyFromObject(obj)
|
||||
receiver := &unstructured.Unstructured{}
|
||||
receiver.SetGroupVersionKind(obj.GroupVersionKind())
|
||||
|
||||
if err := retry.RetryOnConflict(wait.Backoff{
|
||||
Duration: time.Millisecond * 10,
|
||||
Factor: 2,
|
||||
Steps: 3,
|
||||
}, func() error {
|
||||
if err := c.ctrClient.Get(context.Background(), key, receiver); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if err := c.ctrClient.Create(context.Background(), obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := applyOverlay(receiver, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.ctrClient.Update(context.Background(), receiver); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNamespace create namespace
|
||||
func (c *client) CreateNamespace(namespace string) error {
|
||||
key := ctrClient.ObjectKey{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: namespace,
|
||||
}
|
||||
if err := c.ctrClient.Get(context.Background(), key, &corev1.Namespace{}); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
nsObj := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
if err := c.ctrClient.Create(context.Background(), nsObj); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to check if namespace %v exists: %v", namespace, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// KubernetesInterface get kubernetes interface
|
||||
func (c *client) KubernetesInterface() kubernetes.Interface {
|
||||
return c.kube
|
||||
|
||||
}
|
||||
157
hgctl/pkg/kubernetes/common.go
Normal file
157
hgctl/pkg/kubernetes/common.go
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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 kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
kubescheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
)
|
||||
|
||||
// applyOverlay applies an overlay using JSON patch strategy over the current Object in place.
|
||||
func applyOverlay(current, overlay *unstructured.Unstructured) error {
|
||||
cj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, current)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overlayUpdated := overlay.DeepCopy()
|
||||
if strings.EqualFold(current.GetKind(), "service") {
|
||||
if err := saveClusterIP(current, overlayUpdated); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
saveNodePorts(current, overlayUpdated)
|
||||
}
|
||||
|
||||
if current.GetKind() == "PersistentVolumeClaim" {
|
||||
if err := savePersistentVolumeClaim(current, overlayUpdated); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
uj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, overlayUpdated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
merged, err := jsonpatch.MergePatch(cj, uj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runtime.DecodeInto(unstructured.UnstructuredJSONScheme, merged, current)
|
||||
}
|
||||
|
||||
// createPortMap returns a map, mapping the value of the port and value of the nodePort
|
||||
func createPortMap(current *unstructured.Unstructured) map[string]uint32 {
|
||||
portMap := make(map[string]uint32)
|
||||
svc := &corev1.Service{}
|
||||
if err := scheme.Scheme.Convert(current, svc, nil); err != nil {
|
||||
return portMap
|
||||
}
|
||||
for _, p := range svc.Spec.Ports {
|
||||
portMap[strconv.Itoa(int(p.Port))] = uint32(p.NodePort)
|
||||
}
|
||||
return portMap
|
||||
}
|
||||
|
||||
// savePersistentVolumeClaim copies the storageClassName from the current cluster into the overlay
|
||||
func savePersistentVolumeClaim(current, overlay *unstructured.Unstructured) error {
|
||||
// Save the value of spec.storageClassName set by the cluster
|
||||
if storageClassName, found, err := unstructured.NestedString(current.Object, "spec",
|
||||
"storageClassName"); err != nil {
|
||||
return err
|
||||
} else if found {
|
||||
if _, _, err2 := unstructured.NestedString(overlay.Object, "spec",
|
||||
"storageClassName"); err2 != nil {
|
||||
// override when overlay storageClassName property is not existed
|
||||
if err3 := unstructured.SetNestedField(overlay.Object, storageClassName, "spec",
|
||||
"storageClassName"); err3 != nil {
|
||||
return err3
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveNodePorts transfers the port values from the current cluster into the overlay
|
||||
func saveNodePorts(current, overlay *unstructured.Unstructured) {
|
||||
portMap := createPortMap(current)
|
||||
ports, _, _ := unstructured.NestedFieldNoCopy(overlay.Object, "spec", "ports")
|
||||
portList, ok := ports.([]any)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, port := range portList {
|
||||
m, ok := port.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if nodePortNum, ok := m["nodePort"]; ok && fmt.Sprintf("%v", nodePortNum) == "0" {
|
||||
if portNum, ok := m["port"]; ok {
|
||||
if v, ok := portMap[fmt.Sprintf("%v", portNum)]; ok {
|
||||
m["nodePort"] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// saveClusterIP copies the cluster IP from the current cluster into the overlay
|
||||
func saveClusterIP(current, overlay *unstructured.Unstructured) error {
|
||||
// Save the value of spec.clusterIP set by the cluster
|
||||
if clusterIP, found, err := unstructured.NestedString(current.Object, "spec",
|
||||
"clusterIP"); err != nil {
|
||||
return err
|
||||
} else if found {
|
||||
if err := unstructured.SetNestedField(overlay.Object, clusterIP, "spec",
|
||||
"clusterIP"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setRestDefaults(config *rest.Config) *rest.Config {
|
||||
if config.GroupVersion == nil || config.GroupVersion.Empty() {
|
||||
config.GroupVersion = &corev1.SchemeGroupVersion
|
||||
}
|
||||
if len(config.APIPath) == 0 {
|
||||
if len(config.GroupVersion.Group) == 0 {
|
||||
config.APIPath = "/api"
|
||||
} else {
|
||||
config.APIPath = "/apis"
|
||||
}
|
||||
}
|
||||
if len(config.ContentType) == 0 {
|
||||
config.ContentType = runtime.ContentTypeJSON
|
||||
}
|
||||
if config.NegotiatedSerializer == nil {
|
||||
// This codec factory ensures the resources are not converted. Therefore, resources
|
||||
// will not be round-tripped through internal versions. Defaulting does not happen
|
||||
// on the client.
|
||||
config.NegotiatedSerializer = serializer.NewCodecFactory(kubescheme.Scheme).WithoutConversion()
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
160
hgctl/pkg/kubernetes/port-forwarder.go
Normal file
160
hgctl/pkg/kubernetes/port-forwarder.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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 kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/portforward"
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
)
|
||||
|
||||
func LocalAvailablePort(localAddress string) (int, error) {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("%s:0", localAddress))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return l.Addr().(*net.TCPAddr).Port, l.Close()
|
||||
}
|
||||
|
||||
type PortForwarder interface {
|
||||
Start() error
|
||||
|
||||
Stop()
|
||||
|
||||
// Address returns the address of the local forwarded address.
|
||||
Address() string
|
||||
|
||||
// WaitForStop blocks until connection closed (e.g. control-C interrupt)
|
||||
WaitForStop()
|
||||
}
|
||||
|
||||
var _ PortForwarder = &localForwarder{}
|
||||
|
||||
type localForwarder struct {
|
||||
types.NamespacedName
|
||||
CLIClient
|
||||
|
||||
localPort int
|
||||
podPort int
|
||||
localAddress string
|
||||
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func NewLocalPortForwarder(client CLIClient, namespacedName types.NamespacedName, localPort, podPort int, bindAddress string) (PortForwarder, error) {
|
||||
f := &localForwarder{
|
||||
stopCh: make(chan struct{}),
|
||||
CLIClient: client,
|
||||
NamespacedName: namespacedName,
|
||||
localPort: localPort,
|
||||
podPort: podPort,
|
||||
localAddress: bindAddress,
|
||||
}
|
||||
if f.localPort == 0 {
|
||||
// get a random port
|
||||
p, err := LocalAvailablePort(bindAddress)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get a local available port")
|
||||
}
|
||||
f.localPort = p
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *localForwarder) Start() error {
|
||||
errCh := make(chan error, 1)
|
||||
readyCh := make(chan struct{}, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-f.stopCh:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
fw, err := f.buildKubernetesPortForwarder(readyCh)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err := fw.ForwardPorts(); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
readyCh = nil
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return errors.Wrap(err, "failed to start port forwarder")
|
||||
case <-readyCh:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (f *localForwarder) buildKubernetesPortForwarder(readyCh chan struct{}) (*portforward.PortForwarder, error) {
|
||||
restClient, err := rest.RESTClientFor(f.RESTConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := restClient.Post().Resource("pods").Namespace(f.Namespace).Name(f.Name).SubResource("portforward")
|
||||
serverURL := req.URL()
|
||||
|
||||
roundTripper, upgrader, err := spdy.RoundTripperFor(f.RESTConfig())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure creating roundtripper: %v", err)
|
||||
}
|
||||
|
||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, serverURL)
|
||||
fw, err := portforward.NewOnAddresses(dialer,
|
||||
[]string{f.localAddress},
|
||||
[]string{fmt.Sprintf("%d:%d", f.localPort, f.podPort)},
|
||||
f.stopCh,
|
||||
readyCh,
|
||||
io.Discard,
|
||||
os.Stderr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed establishing portforward: %v", err)
|
||||
}
|
||||
|
||||
return fw, nil
|
||||
}
|
||||
|
||||
func (f *localForwarder) Stop() {
|
||||
close(f.stopCh)
|
||||
}
|
||||
|
||||
func (f *localForwarder) Address() string {
|
||||
return fmt.Sprintf("%s:%d", f.localAddress, f.localPort)
|
||||
}
|
||||
|
||||
func (f *localForwarder) WaitForStop() {
|
||||
<-f.stopCh
|
||||
}
|
||||
130
hgctl/pkg/kubernetes/wasmplugin.go
Normal file
130
hgctl/pkg/kubernetes/wasmplugin.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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 kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHigressNamespace = "higress-system"
|
||||
HigressExtGroup = "extensions.higress.io"
|
||||
HigressExtVersion = "v1alpha1"
|
||||
HigressExtAPIVersion = HigressExtGroup + "/" + HigressExtVersion
|
||||
|
||||
WasmPluginKind = "WasmPlugin"
|
||||
WasmPluginResource = "wasmplugins"
|
||||
)
|
||||
|
||||
var (
|
||||
HigressNamespace = DefaultHigressNamespace
|
||||
WasmPluginGVK = schema.GroupVersionKind{Group: HigressExtGroup, Version: HigressExtVersion, Kind: WasmPluginKind}
|
||||
WasmPluginGVR = schema.GroupVersionResource{Group: HigressExtGroup, Version: HigressExtVersion, Resource: WasmPluginResource}
|
||||
)
|
||||
|
||||
func AddHigressNamespaceFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVarP(&HigressNamespace, "namespace", "n",
|
||||
DefaultHigressNamespace, "Namespace where Higress was installed")
|
||||
}
|
||||
|
||||
type WasmPluginClient struct {
|
||||
dyn *DynamicClient
|
||||
}
|
||||
|
||||
func NewWasmPluginClient(dynClient *DynamicClient) *WasmPluginClient {
|
||||
return &WasmPluginClient{dynClient}
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Get(ctx context.Context, name string) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Get(ctx, WasmPluginGVR, HigressNamespace, name)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) List(ctx context.Context) (*unstructured.UnstructuredList, error) {
|
||||
return c.dyn.List(ctx, WasmPluginGVR, HigressNamespace)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Create(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Create(ctx, WasmPluginGVR, HigressNamespace, obj)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Delete(ctx context.Context, name string) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Delete(ctx, WasmPluginGVR, HigressNamespace, name)
|
||||
}
|
||||
|
||||
func (c WasmPluginClient) Update(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.dyn.Update(ctx, WasmPluginGVR, HigressNamespace, obj)
|
||||
}
|
||||
|
||||
// TODO(WeixinX): Will be changed to WasmPlugin specific Client instead of Unstructured
|
||||
type DynamicClient struct {
|
||||
config *rest.Config
|
||||
client dynamic.Interface
|
||||
}
|
||||
|
||||
func NewDynamicClient(clientConfig clientcmd.ClientConfig) (*DynamicClient, error) {
|
||||
var (
|
||||
c DynamicClient
|
||||
err error
|
||||
)
|
||||
|
||||
c.config, err = clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.client, err = dynamic.NewForConfig(c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c DynamicClient) Get(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (c DynamicClient) List(ctx context.Context, gvr schema.GroupVersionResource, namespace string) (*unstructured.UnstructuredList, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).List(ctx, metav1.ListOptions{})
|
||||
}
|
||||
|
||||
func (c DynamicClient) Create(ctx context.Context, gvr schema.GroupVersionResource, namespace string, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).Create(ctx, obj, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
func (c DynamicClient) Delete(ctx context.Context, gvr schema.GroupVersionResource, namespace, name string) (*unstructured.Unstructured, error) {
|
||||
result, err := c.client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.client.Resource(gvr).Namespace(namespace).Delete(ctx, name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c DynamicClient) Update(ctx context.Context, gvr schema.GroupVersionResource, namespace string,
|
||||
obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
return c.client.Resource(gvr).Namespace(namespace).Update(ctx, obj, metav1.UpdateOptions{})
|
||||
}
|
||||
Reference in New Issue
Block a user