mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
* feat: implement output resource existence validation in component, trait, and policy definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add validation tests for ComponentDefinition and TraitDefinition outputs - Implement tests for ComponentDefinition with non-existent CRDs in outputs, ensuring they are rejected. - Add tests for valid outputs in ComponentDefinition, confirming acceptance. - Include tests for mixed valid and non-K8s outputs in ComponentDefinition, verifying they pass validation. - Test handling of empty outputs in ComponentDefinition, ensuring they are accepted. - Introduce tests for invalid apiVersion formats in ComponentDefinition, confirming rejection. - Add tests for TraitDefinition with mixed valid and invalid outputs, ensuring proper rejection. - Create YAML manifests for valid and invalid ComponentDefinitions and TraitDefinitions to support e2e tests. - Ensure comprehensive coverage of edge cases in output validation logic. Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> fix: handle errors in resource validation for component, trait, and policy definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> fix: improve error handling in Go module tidy and resource validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add webhook debugging setup and validation tests for ComponentDefinition and TraitDefinition Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add VS Code launch configuration for debugging webhook validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> refactor: streamline error handling in Go module tidy and remove obsolete test manifests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add mock context support for CUE template compilation Signed-off-by: Reetika Malhotra <malhotra.reetika25@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance validation for WorkflowStepDefinition resources and improve output resource checks Signed-off-by: viskumar <viskumar@guidewire.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: implement resource validation for CUE templates and add unit tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance logging and validation for component, policy, and trait definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: improve error handling and logging in validation handlers for component, policy, trait, and workflow step definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Remove testUnknownResource folder from repository Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: implement structured logging for validation handlers and remove deprecated request_logger Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance structured logging and error handling in admission validation handlers Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: improve logging messages in validating handlers for better clarity Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: refactor logging field definitions for consistency and improve error handling in resource validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> chore: add license header to invalid_resource_check.go and invalid_resource_check_test.go Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance validation tests for WorkflowStepDefinition and improve error messages Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add e2e-test-local target for k3d cluster setup and webhook validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add webhook configuration for workflow step definitions with validation rules Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: update e2e-test-local configuration and improve Ingress API version compatibility Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add installation of FluxCD CRDs in pre-hook to prevent webhook validation errors Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add ValidateResourcesExist feature gate and enhance resource validation in webhook handlers Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance resource validation in e2e tests and improve addon definition checks Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance addon definition detection by using owner references for validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add ValidateResourcesExist feature gate and implement webhook validation for resource existence Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: update Ingress API version to v1 and adjust service references in tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> chore: remove webhook test commands and related YAML files from makefiles and tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> chore: remove architecture section from webhook debugging guide Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: update webhook setup script with k3d host gateway IP note and improve cluster creation logic Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> * Fix: Correct path in Ingress resource definition in template tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> * Chore: add empty line to re-trigger failing workflow Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> * Chore: remove space to re-trigger workflow Signed-off-by: Chaitanya Reddy Onteddu <co@guidewire.com> --------- Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Chaitanya Reddy Onteddu <co@guidewire.com> Co-authored-by: Chaitanya Reddy Onteddu <chaitanyareddy0702@gmail.com> Co-authored-by: Amit Singh <amisingh@guidewire.com>
1031 lines
28 KiB
Go
1031 lines
28 KiB
Go
/*
|
|
Copyright 2021. The KubeVela Authors.
|
|
|
|
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 query
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
v1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
networkv1 "k8s.io/api/networking/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
|
helmapi "github.com/oam-dev/kubevela/pkg/appfile/helm/flux2apis"
|
|
"github.com/oam-dev/kubevela/pkg/oam"
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
|
oamprovidertypes "github.com/oam-dev/kubevela/pkg/workflow/providers/types"
|
|
)
|
|
|
|
type AppResourcesList struct {
|
|
List []Resource `json:"list,omitempty"`
|
|
App interface{} `json:"app"`
|
|
Err string `json:"err,omitempty"`
|
|
}
|
|
|
|
type PodList struct {
|
|
List []*unstructured.Unstructured `json:"list"`
|
|
Value interface{} `json:"value"`
|
|
Cluster string `json:"cluster"`
|
|
}
|
|
|
|
var _ = Describe("Test Query Provider", func() {
|
|
var baseDeploy *v1.Deployment
|
|
var baseService *corev1.Service
|
|
var basePod *corev1.Pod
|
|
|
|
BeforeEach(func() {
|
|
baseDeploy = new(v1.Deployment)
|
|
Expect(yaml.Unmarshal([]byte(deploymentYaml), baseDeploy)).Should(BeNil())
|
|
|
|
baseService = new(corev1.Service)
|
|
Expect(yaml.Unmarshal([]byte(serviceYaml), baseService)).Should(BeNil())
|
|
|
|
basePod = new(corev1.Pod)
|
|
Expect(yaml.Unmarshal([]byte(podYaml), basePod)).Should(BeNil())
|
|
})
|
|
|
|
Context("Test ListResourcesInApp", func() {
|
|
It("Test list latest resources created by application", func() {
|
|
namespace := "test"
|
|
ns := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
|
Expect(k8sClient.Create(ctx, &ns)).Should(BeNil())
|
|
|
|
app := v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test",
|
|
Namespace: "test",
|
|
Annotations: map[string]string{
|
|
oam.AnnotationKubeVelaVersion: "v1.3.1",
|
|
oam.AnnotationPublishVersion: "v1",
|
|
},
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []common.ApplicationComponent{{
|
|
Name: "web",
|
|
Type: "webservice",
|
|
Properties: util.Object2RawExtension(map[string]string{
|
|
"image": "busybox",
|
|
}),
|
|
Traits: []common.ApplicationTrait{{
|
|
Type: "expose",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"ports": []int{8000},
|
|
}),
|
|
}},
|
|
}},
|
|
},
|
|
}
|
|
|
|
Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
|
|
oldApp := new(v1beta1.Application)
|
|
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&app), oldApp)).Should(BeNil())
|
|
oldApp.Status.LatestRevision = &common.Revision{
|
|
Revision: 1,
|
|
}
|
|
oldApp.Status.AppliedResources = []common.ClusterObjectReference{{
|
|
Cluster: "",
|
|
Creator: "workflow",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "v1",
|
|
Kind: "Service",
|
|
Namespace: namespace,
|
|
Name: "web",
|
|
},
|
|
}, {
|
|
Cluster: "",
|
|
Creator: "workflow",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
Namespace: namespace,
|
|
Name: "web",
|
|
},
|
|
}}
|
|
Eventually(func() error {
|
|
err := k8sClient.Status().Update(ctx, oldApp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}, 300*time.Microsecond, 3*time.Second).Should(BeNil())
|
|
|
|
appDeploy := baseDeploy.DeepCopy()
|
|
appDeploy.SetName("web")
|
|
appDeploy.SetNamespace(namespace)
|
|
appDeploy.SetLabels(map[string]string{
|
|
oam.LabelAppComponent: "web",
|
|
oam.LabelAppRevision: "test-v1",
|
|
})
|
|
Expect(k8sClient.Create(ctx, appDeploy)).Should(BeNil())
|
|
|
|
appService := baseService.DeepCopy()
|
|
appService.SetName("web")
|
|
appService.SetNamespace(namespace)
|
|
appService.SetLabels(map[string]string{
|
|
oam.LabelAppComponent: "web",
|
|
oam.LabelAppRevision: "test-v1",
|
|
})
|
|
Expect(k8sClient.Create(ctx, appService)).Should(BeNil())
|
|
|
|
rt := &v1beta1.ResourceTracker{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: fmt.Sprintf("%s-v1-%s", oldApp.Name, oldApp.Namespace),
|
|
Labels: map[string]string{
|
|
oam.LabelAppName: oldApp.Name,
|
|
oam.LabelAppNamespace: oldApp.Namespace,
|
|
},
|
|
Annotations: map[string]string{
|
|
oam.AnnotationPublishVersion: "v1",
|
|
},
|
|
},
|
|
Spec: v1beta1.ResourceTrackerSpec{
|
|
ManagedResources: []v1beta1.ManagedResource{
|
|
{
|
|
ClusterObjectReference: common.ClusterObjectReference{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "v1",
|
|
Kind: "Service",
|
|
Namespace: namespace,
|
|
Name: "web",
|
|
},
|
|
},
|
|
OAMObjectReference: common.OAMObjectReference{
|
|
Component: "web",
|
|
},
|
|
},
|
|
{
|
|
ClusterObjectReference: common.ClusterObjectReference{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
Namespace: namespace,
|
|
Name: "web",
|
|
},
|
|
},
|
|
OAMObjectReference: common.OAMObjectReference{
|
|
Component: "web",
|
|
},
|
|
},
|
|
},
|
|
Type: v1beta1.ResourceTrackerTypeVersioned,
|
|
},
|
|
}
|
|
Expect(k8sClient.Create(ctx, rt)).Should(BeNil())
|
|
|
|
params := &ListParams{
|
|
Params: ListVars{
|
|
App: Option{
|
|
Name: "test",
|
|
Namespace: "test",
|
|
Filter: FilterOption{
|
|
Cluster: "",
|
|
ClusterNamespace: "test",
|
|
Components: []string{"web"},
|
|
},
|
|
},
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
},
|
|
}
|
|
res, err := ListResourcesInApp(context.Background(), params)
|
|
Expect(err).Should(BeNil())
|
|
resList := (*res).Returns.List
|
|
Expect(len(resList)).Should(Equal(2))
|
|
|
|
Expect(resList[0].Object.GroupVersionKind()).Should(Equal(oldApp.Status.AppliedResources[0].GroupVersionKind()))
|
|
Expect(resList[1].Object.GroupVersionKind()).Should(Equal(oldApp.Status.AppliedResources[1].GroupVersionKind()))
|
|
|
|
updateApp := new(v1beta1.Application)
|
|
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&app), updateApp)).Should(BeNil())
|
|
|
|
updateApp.ObjectMeta.Annotations = map[string]string{
|
|
oam.AnnotationKubeVelaVersion: "v1.1.0",
|
|
}
|
|
Expect(k8sClient.Update(ctx, updateApp)).Should(BeNil())
|
|
res, err = ListResourcesInApp(context.Background(), params)
|
|
Expect(err).Should(BeNil())
|
|
resList = (*res).Returns.List
|
|
Expect(len(resList)).Should(Equal(2))
|
|
Expect(resList[0].Object.GroupVersionKind()).Should(Equal(updateApp.Status.AppliedResources[0].GroupVersionKind()))
|
|
Expect(resList[1].Object.GroupVersionKind()).Should(Equal(updateApp.Status.AppliedResources[1].GroupVersionKind()))
|
|
})
|
|
})
|
|
|
|
Context("Test ListAppliedResources", func() {
|
|
It("Test list applied resources created by application", func() {
|
|
// create test app
|
|
app := v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-applied",
|
|
Namespace: "default",
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []common.ApplicationComponent{{
|
|
Name: "web",
|
|
Type: "webservice",
|
|
Properties: util.Object2RawExtension(map[string]string{
|
|
"image": "busybox",
|
|
}),
|
|
Traits: []common.ApplicationTrait{{
|
|
Type: "expose",
|
|
Properties: util.Object2RawExtension(map[string]interface{}{
|
|
"ports": []int{8000},
|
|
}),
|
|
}},
|
|
}},
|
|
},
|
|
}
|
|
Expect(k8sClient.Create(ctx, &app)).Should(BeNil())
|
|
// create RT
|
|
rt := &v1beta1.ResourceTracker{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-applied",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
oam.LabelAppName: app.Name,
|
|
oam.LabelAppNamespace: app.Namespace,
|
|
},
|
|
},
|
|
Spec: v1beta1.ResourceTrackerSpec{
|
|
Type: v1beta1.ResourceTrackerTypeRoot,
|
|
ManagedResources: []v1beta1.ManagedResource{
|
|
{
|
|
ClusterObjectReference: common.ClusterObjectReference{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
Kind: "Deployment",
|
|
APIVersion: "apps/v1",
|
|
Namespace: "default",
|
|
Name: "web",
|
|
},
|
|
},
|
|
OAMObjectReference: common.OAMObjectReference{
|
|
Component: "web",
|
|
},
|
|
},
|
|
{
|
|
ClusterObjectReference: common.ClusterObjectReference{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
Kind: "Service",
|
|
APIVersion: "v1",
|
|
Namespace: "default",
|
|
Name: "web",
|
|
},
|
|
},
|
|
OAMObjectReference: common.OAMObjectReference{
|
|
Trait: "expose",
|
|
Component: "web",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := k8sClient.Create(context.TODO(), rt)
|
|
Expect(err).Should(BeNil())
|
|
|
|
params := &ListParams{
|
|
Params: ListVars{
|
|
App: Option{
|
|
Name: "test-applied",
|
|
Namespace: "default",
|
|
Filter: FilterOption{
|
|
Components: []string{"web"},
|
|
},
|
|
},
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
},
|
|
}
|
|
res, err := ListAppliedResources(context.Background(), params)
|
|
Expect(err).Should(BeNil())
|
|
resList := (*res).Returns.List
|
|
Expect(len(resList)).Should(Equal(2))
|
|
|
|
By("test filter with the apiVersion and kind")
|
|
params.Params.App.Filter.APIVersion = "apps/v1"
|
|
res2, err := ListAppliedResources(context.Background(), params)
|
|
Expect(err).Should(BeNil())
|
|
resList2 := (*res2).Returns.List
|
|
Expect(len(resList2)).Should(Equal(1))
|
|
Expect(resList2[0].Kind).Should(Equal("Deployment"))
|
|
|
|
params.Params.App.Filter.Kind = "Service"
|
|
params.Params.App.Filter.APIVersion = ""
|
|
res3, err := ListAppliedResources(context.Background(), params)
|
|
Expect(err).Should(BeNil())
|
|
resList3 := (*res3).Returns.List
|
|
Expect(len(resList3)).Should(Equal(1))
|
|
Expect(resList3[0].Kind).Should((Equal("Service")))
|
|
})
|
|
})
|
|
|
|
Context("Test search event from k8s object", func() {
|
|
It("Test search event with incomplete parameter", func() {
|
|
_, err := SearchEvents(context.Background(), &SearchParams{})
|
|
Expect(err).ShouldNot(BeNil())
|
|
|
|
_, err = SearchEvents(context.Background(), &SearchParams{
|
|
Params: SearchVars{
|
|
Cluster: "test",
|
|
},
|
|
})
|
|
Expect(err).ShouldNot(BeNil())
|
|
})
|
|
})
|
|
|
|
Context("Test CollectLogsInPod", func() {
|
|
It("Test CollectLogsInPod with specified container", func() {
|
|
pod := &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "hello-world", Namespace: "default"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{{Name: "main", Image: "busybox"}},
|
|
}}
|
|
Expect(k8sClient.Create(ctx, pod)).Should(Succeed())
|
|
|
|
_, err := CollectLogsInPod(context.Background(), &LogParams{})
|
|
Expect(err).ShouldNot(BeNil())
|
|
|
|
_, err = CollectLogsInPod(context.Background(), &LogParams{
|
|
Params: LogVars{
|
|
Cluster: "local",
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
KubeConfig: cfg,
|
|
},
|
|
})
|
|
Expect(err).ShouldNot(BeNil())
|
|
|
|
_, err = CollectLogsInPod(context.Background(), &LogParams{
|
|
Params: LogVars{
|
|
Cluster: "local",
|
|
Namespace: "default",
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
KubeConfig: cfg,
|
|
},
|
|
})
|
|
Expect(err).ShouldNot(BeNil())
|
|
|
|
_, err = CollectLogsInPod(context.Background(), &LogParams{
|
|
Params: LogVars{
|
|
Cluster: "local",
|
|
Namespace: "default",
|
|
Pod: "hello-world",
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
KubeConfig: cfg,
|
|
},
|
|
})
|
|
Expect(err).ShouldNot(BeNil())
|
|
|
|
_, err = CollectLogsInPod(context.Background(), &LogParams{
|
|
Params: LogVars{
|
|
Cluster: "local",
|
|
Namespace: "default",
|
|
Pod: "hello-world",
|
|
Options: &corev1.PodLogOptions{
|
|
Container: "main",
|
|
Previous: true,
|
|
},
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
KubeConfig: cfg,
|
|
},
|
|
})
|
|
Expect(err).Should(Succeed())
|
|
})
|
|
})
|
|
|
|
It("Test generator service endpoints", func() {
|
|
appsts := common.AppStatus{
|
|
AppliedResources: []common.ClusterObjectReference{
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
Kind: "Ingress",
|
|
Namespace: "default",
|
|
Name: "ingress-http",
|
|
APIVersion: "networking.k8s.io/v1",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
Kind: "Ingress",
|
|
Namespace: "default",
|
|
Name: "ingress-https",
|
|
APIVersion: "networking.k8s.io/v1",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
Kind: "Ingress",
|
|
Namespace: "default",
|
|
Name: "ingress-paths",
|
|
APIVersion: "networking.k8s.io/v1",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "v1",
|
|
Kind: "Service",
|
|
Namespace: "default",
|
|
Name: "nodeport",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "v1",
|
|
Kind: "Service",
|
|
Namespace: "default",
|
|
Name: "loadbalancer",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "helm.toolkit.fluxcd.io/v2beta1",
|
|
Kind: helmapi.HelmReleaseGVK.Kind,
|
|
Namespace: "default",
|
|
Name: "helm-release",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "machinelearning.seldon.io/v1",
|
|
Kind: "SeldonDeployment",
|
|
Namespace: "default",
|
|
Name: "sdep",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "gateway.networking.k8s.io/v1beta1",
|
|
Kind: "HTTPRoute",
|
|
Namespace: "default",
|
|
Name: "http-test-route",
|
|
},
|
|
},
|
|
{
|
|
Cluster: "",
|
|
ObjectReference: corev1.ObjectReference{
|
|
APIVersion: "gateway.networking.k8s.io/v1beta1",
|
|
Kind: "HTTPRoute",
|
|
Namespace: "default",
|
|
Name: "velaux-ssl",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
testApp := &v1beta1.Application{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "endpoints-app",
|
|
Namespace: "default",
|
|
},
|
|
Spec: v1beta1.ApplicationSpec{
|
|
Components: []common.ApplicationComponent{
|
|
{
|
|
Name: "endpoints-test",
|
|
Type: "webservice",
|
|
},
|
|
},
|
|
},
|
|
Status: appsts,
|
|
}
|
|
err := k8sClient.Create(context.TODO(), testApp)
|
|
Expect(err).Should(BeNil())
|
|
|
|
var gtapp v1beta1.Application
|
|
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "endpoints-app", Namespace: "default"}, >app)).Should(BeNil())
|
|
gtapp.Status = appsts
|
|
Expect(k8sClient.Status().Update(ctx, >app)).Should(BeNil())
|
|
var mr []v1beta1.ManagedResource
|
|
for _, ar := range appsts.AppliedResources {
|
|
smr := v1beta1.ManagedResource{
|
|
ClusterObjectReference: ar,
|
|
}
|
|
smr.Component = "endpoints-test"
|
|
mr = append(mr, smr)
|
|
}
|
|
rt := &v1beta1.ResourceTracker{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "endpoints-app",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
oam.LabelAppName: testApp.Name,
|
|
oam.LabelAppNamespace: testApp.Namespace,
|
|
},
|
|
},
|
|
Spec: v1beta1.ResourceTrackerSpec{
|
|
Type: v1beta1.ResourceTrackerTypeRoot,
|
|
ManagedResources: mr,
|
|
},
|
|
}
|
|
err = k8sClient.Create(context.TODO(), rt)
|
|
Expect(err).Should(BeNil())
|
|
|
|
helmRelease := &unstructured.Unstructured{}
|
|
helmRelease.SetName("helm-release")
|
|
helmRelease.SetNamespace("default")
|
|
helmRelease.SetGroupVersionKind(helmapi.HelmReleaseGVK)
|
|
err = k8sClient.Create(context.TODO(), helmRelease)
|
|
Expect(err).Should(BeNil())
|
|
|
|
testServiceList := []map[string]interface{}{
|
|
{
|
|
"name": "clusterip",
|
|
"ports": []corev1.ServicePort{
|
|
{Port: 80, TargetPort: intstr.FromInt(80), Name: "80port"},
|
|
{Port: 81, TargetPort: intstr.FromInt(81), Name: "81port"},
|
|
},
|
|
"type": corev1.ServiceTypeClusterIP,
|
|
},
|
|
{
|
|
"name": "nodeport",
|
|
"ports": []corev1.ServicePort{
|
|
{Port: 80, TargetPort: intstr.FromInt(80), NodePort: 30229},
|
|
},
|
|
"type": corev1.ServiceTypeNodePort,
|
|
},
|
|
{
|
|
"name": "loadbalancer",
|
|
"ports": []corev1.ServicePort{
|
|
{Port: 80, TargetPort: intstr.FromInt(80), Name: "80port", NodePort: 30080},
|
|
{Port: 81, TargetPort: intstr.FromInt(81), Name: "81port", NodePort: 30081},
|
|
},
|
|
"type": corev1.ServiceTypeLoadBalancer,
|
|
"status": corev1.ServiceStatus{
|
|
LoadBalancer: corev1.LoadBalancerStatus{
|
|
Ingress: []corev1.LoadBalancerIngress{
|
|
{
|
|
IP: "10.10.10.10",
|
|
},
|
|
{
|
|
Hostname: "text.example.com",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"name": "helm1",
|
|
"ports": []corev1.ServicePort{
|
|
{Port: 80, NodePort: 30002, TargetPort: intstr.FromInt(80)},
|
|
},
|
|
"type": corev1.ServiceTypeNodePort,
|
|
"labels": map[string]string{
|
|
"helm.toolkit.fluxcd.io/name": "helm-release",
|
|
"helm.toolkit.fluxcd.io/namespace": "default",
|
|
},
|
|
},
|
|
{
|
|
"name": "seldon-ambassador",
|
|
"ports": []corev1.ServicePort{
|
|
{Port: 80, TargetPort: intstr.FromInt(80), Name: "80port", NodePort: 30011},
|
|
},
|
|
"type": corev1.ServiceTypeLoadBalancer,
|
|
"status": corev1.ServiceStatus{
|
|
LoadBalancer: corev1.LoadBalancerStatus{
|
|
Ingress: []corev1.LoadBalancerIngress{
|
|
{
|
|
IP: "1.1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err = k8sClient.Create(context.TODO(), &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "vela-system",
|
|
},
|
|
})
|
|
Expect(err).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
|
for _, s := range testServiceList {
|
|
ns := "default"
|
|
if s["namespace"] != nil {
|
|
ns = s["namespace"].(string)
|
|
}
|
|
service := &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: s["name"].(string),
|
|
Namespace: ns,
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Ports: s["ports"].([]corev1.ServicePort),
|
|
Type: s["type"].(corev1.ServiceType),
|
|
},
|
|
}
|
|
|
|
if s["labels"] != nil {
|
|
service.Labels = s["labels"].(map[string]string)
|
|
}
|
|
err := k8sClient.Create(context.TODO(), service)
|
|
Expect(err).Should(BeNil())
|
|
if s["status"] != nil {
|
|
service.Status = s["status"].(corev1.ServiceStatus)
|
|
err := k8sClient.Status().Update(context.TODO(), service)
|
|
Expect(err).Should(BeNil())
|
|
}
|
|
}
|
|
|
|
var prefixbeta = networkv1.PathTypePrefix
|
|
testIngress := []client.Object{
|
|
&networkv1.Ingress{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "ingress-http",
|
|
Namespace: "default",
|
|
},
|
|
Spec: networkv1.IngressSpec{
|
|
Rules: []networkv1.IngressRule{
|
|
{
|
|
Host: "ingress.domain",
|
|
IngressRuleValue: networkv1.IngressRuleValue{
|
|
HTTP: &networkv1.HTTPIngressRuleValue{
|
|
Paths: []networkv1.HTTPIngressPath{
|
|
{
|
|
Path: "/",
|
|
Backend: networkv1.IngressBackend{
|
|
Service: &networkv1.IngressServiceBackend{
|
|
Name: "clusterip",
|
|
Port: networkv1.ServiceBackendPort{
|
|
Number: 80,
|
|
},
|
|
},
|
|
},
|
|
PathType: &prefixbeta,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
&networkv1.Ingress{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "ingress-https",
|
|
Namespace: "default",
|
|
},
|
|
Spec: networkv1.IngressSpec{
|
|
TLS: []networkv1.IngressTLS{
|
|
{
|
|
SecretName: "https-secret",
|
|
},
|
|
},
|
|
Rules: []networkv1.IngressRule{
|
|
{
|
|
Host: "ingress.domain.https",
|
|
IngressRuleValue: networkv1.IngressRuleValue{
|
|
HTTP: &networkv1.HTTPIngressRuleValue{
|
|
Paths: []networkv1.HTTPIngressPath{
|
|
{
|
|
Path: "/",
|
|
Backend: networkv1.IngressBackend{
|
|
Service: &networkv1.IngressServiceBackend{
|
|
Name: "clusterip",
|
|
Port: networkv1.ServiceBackendPort{
|
|
Number: 80,
|
|
},
|
|
},
|
|
},
|
|
PathType: &prefixbeta,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
&networkv1.Ingress{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "ingress-paths",
|
|
Namespace: "default",
|
|
},
|
|
Spec: networkv1.IngressSpec{
|
|
TLS: []networkv1.IngressTLS{
|
|
{
|
|
SecretName: "https-secret",
|
|
},
|
|
},
|
|
Rules: []networkv1.IngressRule{
|
|
{
|
|
Host: "ingress.domain.path",
|
|
IngressRuleValue: networkv1.IngressRuleValue{
|
|
HTTP: &networkv1.HTTPIngressRuleValue{
|
|
Paths: []networkv1.HTTPIngressPath{
|
|
{
|
|
Path: "/test",
|
|
Backend: networkv1.IngressBackend{
|
|
Service: &networkv1.IngressServiceBackend{
|
|
Name: "clusterip",
|
|
Port: networkv1.ServiceBackendPort{
|
|
Number: 80,
|
|
},
|
|
},
|
|
},
|
|
PathType: &prefixbeta,
|
|
},
|
|
{
|
|
Path: "/test2",
|
|
Backend: networkv1.IngressBackend{
|
|
Service: &networkv1.IngressServiceBackend{
|
|
Name: "clusterip",
|
|
Port: networkv1.ServiceBackendPort{
|
|
Number: 80,
|
|
},
|
|
},
|
|
},
|
|
PathType: &prefixbeta,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
&networkv1.Ingress{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "networking.k8s.io/v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "ingress-helm",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"helm.toolkit.fluxcd.io/name": "helm-release",
|
|
"helm.toolkit.fluxcd.io/namespace": "default",
|
|
},
|
|
},
|
|
Spec: networkv1.IngressSpec{
|
|
Rules: []networkv1.IngressRule{
|
|
{
|
|
Host: "ingress.domain.helm",
|
|
IngressRuleValue: networkv1.IngressRuleValue{
|
|
HTTP: &networkv1.HTTPIngressRuleValue{
|
|
Paths: []networkv1.HTTPIngressPath{
|
|
{
|
|
Path: "/",
|
|
Backend: networkv1.IngressBackend{
|
|
Service: &networkv1.IngressServiceBackend{
|
|
Name: "clusterip",
|
|
Port: networkv1.ServiceBackendPort{
|
|
Number: 80,
|
|
},
|
|
},
|
|
},
|
|
PathType: &prefixbeta,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, ing := range testIngress {
|
|
err := k8sClient.Create(context.TODO(), ing)
|
|
Expect(err).Should(BeNil())
|
|
}
|
|
|
|
obj := &unstructured.Unstructured{}
|
|
obj.SetName("sdep")
|
|
obj.SetNamespace("default")
|
|
obj.SetAnnotations(map[string]string{
|
|
annoAmbassadorServiceName: "seldon-ambassador",
|
|
annoAmbassadorServiceNamespace: "default",
|
|
})
|
|
obj.SetGroupVersionKind(schema.GroupVersionKind{
|
|
Group: "machinelearning.seldon.io",
|
|
Version: "v1",
|
|
Kind: "SeldonDeployment",
|
|
})
|
|
err = k8sClient.Create(context.TODO(), obj)
|
|
Expect(err).Should(BeNil())
|
|
|
|
// Create the HTTPRoute for test
|
|
resources := []string{
|
|
"./testdata/gateway/http-route.yaml",
|
|
"./testdata/gateway/gateway.yaml",
|
|
"./testdata/gateway/gateway-tls.yaml",
|
|
"./testdata/gateway/https-route.yaml",
|
|
}
|
|
var objects []client.Object
|
|
for _, resource := range resources {
|
|
data, err := os.ReadFile(resource)
|
|
Expect(err).Should(BeNil())
|
|
var route unstructured.Unstructured
|
|
err = yaml.Unmarshal(data, &route)
|
|
Expect(err).Should(BeNil())
|
|
objects = append(objects, &route)
|
|
}
|
|
|
|
for _, res := range objects {
|
|
err := k8sClient.Create(context.TODO(), res)
|
|
Expect(err).Should(BeNil())
|
|
}
|
|
|
|
// Prepare nodes in test environment
|
|
masterNode := &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-1",
|
|
Labels: map[string]string{
|
|
"node-role.kubernetes.io/master": "true",
|
|
},
|
|
},
|
|
Status: corev1.NodeStatus{
|
|
Addresses: []corev1.NodeAddress{
|
|
{
|
|
Type: corev1.NodeInternalIP,
|
|
Address: "internal-ip-1",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
workerNode := &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-2",
|
|
Labels: map[string]string{
|
|
"node-role.kubernetes.io/worker": "true",
|
|
},
|
|
},
|
|
Status: corev1.NodeStatus{
|
|
Addresses: []corev1.NodeAddress{
|
|
{
|
|
Type: corev1.NodeInternalIP,
|
|
Address: "internal-ip-2",
|
|
},
|
|
{
|
|
Type: corev1.NodeExternalIP,
|
|
Address: "external-ip-2",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
Expect(k8sClient.Create(ctx, masterNode)).Should(BeNil())
|
|
Expect(k8sClient.Create(ctx, workerNode)).Should(BeNil())
|
|
|
|
params := &ListParams{
|
|
Params: ListVars{
|
|
App: Option{
|
|
Name: "endpoints-app",
|
|
Namespace: "default",
|
|
Filter: FilterOption{
|
|
Cluster: "",
|
|
ClusterNamespace: "default",
|
|
},
|
|
WithTree: true,
|
|
},
|
|
},
|
|
RuntimeParams: oamprovidertypes.RuntimeParams{
|
|
KubeClient: k8sClient,
|
|
},
|
|
}
|
|
res, err := CollectServiceEndpoints(context.Background(), params)
|
|
Expect(err).Should(BeNil())
|
|
|
|
gatewayIP := selectorNodeIP(ctx, "", k8sClient)
|
|
Expect(gatewayIP).Should(Equal("external-ip-2"))
|
|
urls := []string{
|
|
"http://ingress.domain",
|
|
"https://ingress.domain.https",
|
|
"https://ingress.domain.path/test",
|
|
"https://ingress.domain.path/test2",
|
|
fmt.Sprintf("http://%s:30229", gatewayIP),
|
|
"http://10.10.10.10",
|
|
"http://text.example.com",
|
|
"10.10.10.10:81",
|
|
"text.example.com:81",
|
|
fmt.Sprintf("http://%s:30002", gatewayIP),
|
|
"http://ingress.domain.helm",
|
|
"http://1.1.1.1/seldon/default/sdep",
|
|
"http://gateway.domain",
|
|
"http://gateway.domain/api",
|
|
"https://demo.kubevela.net",
|
|
}
|
|
for i, e := range (*res).Returns.List {
|
|
Expect(urls[i]).Should(Equal(e.String()))
|
|
}
|
|
})
|
|
})
|
|
|
|
var deploymentYaml = `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
labels:
|
|
app.oam.dev/app-revision-hash: ee69f7ed168cd8fa
|
|
app.oam.dev/appRevision: first-vela-app-v1
|
|
app.oam.dev/component: express-server
|
|
app.oam.dev/name: first-vela-app
|
|
app.oam.dev/resourceType: WORKLOAD
|
|
app.oam.dev/revision: express-server-v1
|
|
oam.dev/render-hash: ee2d39b553b6ef03
|
|
workload.oam.dev/type: webservice
|
|
name: express-server
|
|
namespace: default
|
|
spec:
|
|
replicas: 2
|
|
selector:
|
|
matchLabels:
|
|
app.oam.dev/component: express-server
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.oam.dev/component: express-server
|
|
spec:
|
|
containers:
|
|
- image: crccheck/hello-world
|
|
imagePullPolicy: Always
|
|
name: express-server
|
|
ports:
|
|
- containerPort: 8000
|
|
protocol: TCP
|
|
`
|
|
|
|
var serviceYaml = `
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
labels:
|
|
app.oam.dev/app-revision-hash: ee69f7ed168cd8fa
|
|
app.oam.dev/appRevision: first-vela-app-v1
|
|
app.oam.dev/component: express-server
|
|
app.oam.dev/name: first-vela-app
|
|
app.oam.dev/resourceType: TRAIT
|
|
app.oam.dev/revision: express-server-v1
|
|
oam.dev/render-hash: bebe99ac3e9607d0
|
|
trait.oam.dev/resource: service
|
|
trait.oam.dev/type: ingress-1-20
|
|
name: express-server
|
|
namespace: default
|
|
spec:
|
|
ports:
|
|
- port: 8000
|
|
protocol: TCP
|
|
targetPort: 8000
|
|
selector:
|
|
app.oam.dev/component: express-server
|
|
`
|
|
|
|
var podYaml = `
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
labels:
|
|
app.oam.dev/component: express-server
|
|
name: express-server-b77f4476b-4mt5m
|
|
namespace: default
|
|
spec:
|
|
containers:
|
|
- image: crccheck/hello-world
|
|
imagePullPolicy: Always
|
|
name: express-server-1
|
|
ports:
|
|
- containerPort: 8000
|
|
protocol: TCP
|
|
`
|