updated oam-k8s-runtime and go through rollout fix bugs

Signed-off-by: 天元 <jianbo.sjb@alibaba-inc.com>
This commit is contained in:
天元
2020-11-12 20:02:04 +08:00
parent ccc3576a4c
commit cd4615405f
15 changed files with 171 additions and 30 deletions

View File

@@ -126,10 +126,12 @@ core-run: generate fmt vet manifests
# Install CRDs and Definitions of Vela Core into a cluster, this is for develop convenient.
core-install: manifests
kubectl apply -f hack/namespace.yaml
kubectl apply -f charts/vela-core/crds/
kubectl apply -f charts/vela-core/templates/defwithtemplate/
kubectl apply -f charts/vela-core/templates/definitions/
bin/vela system update
kubectl apply -f charts/vela-core/templates/velaConfig.yaml
bin/vela workloads
# Uninstall CRDs and Definitions of Vela Core from a cluster, this is for develop convenient.
core-uninstall: manifests

View File

@@ -2,7 +2,8 @@ apiVersion: v1
kind: ConfigMap
metadata:
name: vela-config
namespace: {{ .Release.Namespace }}
# TODO: Currently namespace MUST be vela-system
namespace: vela-system
data:
servicemonitors.monitoring.coreos.com: |
{

View File

@@ -9,8 +9,8 @@ name: testapp
services:
express-server:
type: webservice
image: oamdev/testapp:v1
port: 8080
image: oamdev/testapp:rolling01
port: 80
rollout:
replica: 5
@@ -18,7 +18,16 @@ services:
interval: "30s"
route:
domain: example.com
domain: "example.com"
```
If your cluster don't have ingress, you could set domain to be empty like below:
```yaml
...
route:
- domain: "example.com"
+ domain: ""
```
> The full specification of `rollout` could be found [here](references/traits/rollout.md)
@@ -60,9 +69,22 @@ Visiting this app by:
```bash
$ curl -H "Host:example.com" http://<your-ingress-IP-address>/
Hello World%
Hello World -- Rolling 01
```
If you don't have ingress in your cluster and you leave domain to be empty, then you could visit this app by:
```bash
$ vela port-forward testapp --route
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Forward successfully! Opening browser ...
Handling connection for 8080
```
It will automatically open browser for you.
In day 2, assuming we have make some changes on our app and build the new image and name it by `oamdev/testapp:v2`.
Let's update the appfile by:
@@ -72,9 +94,9 @@ name: testapp
services:
express-server:
type: webservice
- image: oamdev/testapp:v1
+ image: oamdev/testapp:v2
port: 8080
- image: oamdev/testapp:rolling01
+ image: oamdev/testapp:rolling02
port: 80
rollout:
replica: 5
stepWeight: 20
@@ -89,22 +111,51 @@ Apply this `appfile.yaml` again:
$ vela up
```
You could then try to `curl` your app multiple times and and see how the new instances being promoted following Canary rollout strategy:
You could run `vela status` several times to see the instance rolling:
```shell script
$ vela status testapp
About:
Name: testapp
Namespace: myenv
Created at: 2020-11-12 19:02:40.353693 +0800 CST
Updated at: 2020-11-12 19:02:40.353693 +0800 CST
Services:
- Name: express-server
Type: webservice
HEALTHY express-server-v2:Ready: 1/1 express-server-v1:Ready: 4/4
Traits:
- ✅ rollout: interval=30s
replica=5
stepWeight=20
- ✅ route: Visiting by using 'vela port-forward testapp --route'
Last Deployment:
Created at: 2020-11-12 17:20:46 +0800 CST
Updated at: 2020-11-12T19:02:40+08:00
```
You could then try to `curl` your app multiple times and and see how the new instances being promoted following Canary
rollout strategy: (Note: Using `vela port-forward` will not see this as port-forward will proxy network on fixed port, only ingress
has loadbalance.)
```bash
$ curl -H "Host:example.com" http://<your-ingress-ip-address>/
Hello World -- Updated Version Two!%
Hello World -- This is rolling 02
$ curl -H "Host:example.com" http://<your-ingress-ip-address>/
Hello World%
Hello World -- Rolling 01
$ curl -H "Host:example.com" http://<your-ingress-ip-address>/
Hello World%
Hello World -- Rolling 01
$ curl -H "Host:example.com" http://<your-ingress-ip-address>/
Hello World -- Updated Version Two!%
Hello World -- This is rolling 02
$ curl -H "Host:example.com" http://<your-ingress-ip-address>/
Hello World%
Hello World -- Rolling 01
$ curl -H "Host:example.com" http://<your-ingress-ip-address>/
Hello World -- Updated Version Two!%
Hello World -- This is rolling 02
```
For every 30 second, 20% more traffic will be shifted to the new instance from the old instance as we configured in Appfile.

2
go.mod
View File

@@ -9,7 +9,7 @@ require (
github.com/briandowns/spinner v1.11.1
github.com/coreos/prometheus-operator v0.41.1
github.com/crossplane/crossplane-runtime v0.10.0
github.com/crossplane/oam-kubernetes-runtime v0.3.2
github.com/crossplane/oam-kubernetes-runtime v0.3.3-0.20201112082656-22b7738dcdf3
github.com/fatih/color v1.9.0
github.com/gertd/go-pluralize v0.1.7
github.com/ghodss/yaml v1.0.0

4
go.sum
View File

@@ -414,8 +414,8 @@ github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/crossplane/crossplane-runtime v0.10.0 h1:H8YvMcrm1uzZYpwU/BpxjRQfceVulxgYJMx4rmX38Hg=
github.com/crossplane/crossplane-runtime v0.10.0/go.mod h1:cJl5ZZONisre4v6wTmbrC8Jh3AI+erq/lNaxZzv9tnU=
github.com/crossplane/oam-kubernetes-runtime v0.3.2 h1:iUBsYYn+33X1liRm6sn7oUA2hoXCWW8ik5QtATLZNxk=
github.com/crossplane/oam-kubernetes-runtime v0.3.2/go.mod h1:K4/F1XOPBvmW/PaRSPL3wNA4kCrFGUQC7WkBYcwIGx8=
github.com/crossplane/oam-kubernetes-runtime v0.3.3-0.20201112082656-22b7738dcdf3 h1:0AMLxvQoU14r3qTvGZ8olSwvBq7McL1ZGdiHAQ7L8M8=
github.com/crossplane/oam-kubernetes-runtime v0.3.3-0.20201112082656-22b7738dcdf3/go.mod h1:K4/F1XOPBvmW/PaRSPL3wNA4kCrFGUQC7WkBYcwIGx8=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/daixiang0/gci v0.0.0-20200727065011-66f1df783cb2/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4=

4
hack/namespace.yaml Normal file
View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: vela-system

View File

@@ -1,6 +1,7 @@
package application
import (
"context"
"errors"
"fmt"
"io/ioutil"
@@ -11,6 +12,8 @@ import (
"strings"
"time"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/crossplane/oam-kubernetes-runtime/pkg/oam"
"github.com/ghodss/yaml"
@@ -234,3 +237,11 @@ func getApplicationDir(envName string) (string, error) {
_, err := system.CreateIfNotExist(appDir)
return appDir, err
}
func GetAppConfig(ctx context.Context, c client.Client, app *Application, env *types.EnvMeta) (*v1alpha2.ApplicationConfiguration, error) {
appConfig := &v1alpha2.ApplicationConfiguration{}
if err := c.Get(ctx, client.ObjectKey{Namespace: env.Namespace, Name: app.Name}, appConfig); err != nil {
return nil, err
}
return appConfig, nil
}

View File

@@ -9,6 +9,13 @@ import (
"strconv"
"strings"
corev1 "k8s.io/api/core/v1"
types2 "k8s.io/apimachinery/pkg/types"
"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/crossplane/oam-kubernetes-runtime/pkg/oam"
"github.com/oam-dev/kubevela/api/types"
"github.com/spf13/cobra"
@@ -39,6 +46,8 @@ type VelaPortForwardOptions struct {
f k8scmdutil.Factory
kcPortForwardOptions *cmdpf.PortForwardOptions
ClientSet kubernetes.Interface
Client client.Client
routeTrait bool
}
func NewPortForwardCommand(c types.Args, ioStreams velacmdutil.IOStreams) *cobra.Command {
@@ -49,6 +58,7 @@ func NewPortForwardCommand(c types.Args, ioStreams velacmdutil.IOStreams) *cobra
PortForwarder: &defaultPortForwarder{ioStreams},
},
}
cmd := &cobra.Command{
Use: "port-forward APP_NAME [options] [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
Short: "Forward one or more local ports to a Pod of a service in an application",
@@ -58,6 +68,11 @@ func NewPortForwardCommand(c types.Args, ioStreams velacmdutil.IOStreams) *cobra
ioStreams.Error("Please specify application name.")
return nil
}
newClient, err := client.New(o.VelaC.Config, client.Options{Scheme: o.VelaC.Schema})
if err != nil {
return err
}
o.Client = newClient
if err := o.Init(context.Background(), cmd, args); err != nil {
return err
}
@@ -77,6 +92,7 @@ func NewPortForwardCommand(c types.Args, ioStreams velacmdutil.IOStreams) *cobra
cmd.Flags().Duration(podRunningTimeoutFlag, defaultPodExecTimeout,
"The length of time (like 5s, 2m, or 3h, higher than zero) to wait until at least one pod is running",
)
cmd.Flags().BoolVar(&o.routeTrait, "route", false, "forward ports from route trait service")
return cmd
}
@@ -111,11 +127,56 @@ func (o *VelaPortForwardOptions) Init(ctx context.Context, cmd *cobra.Command, a
return nil
}
func GetRouteServiceName(appconfig *v1alpha2.ApplicationConfiguration, svcName string) string {
for _, comp := range appconfig.Status.Workloads {
if comp.ComponentName != svcName {
continue
}
for _, tr := range comp.Traits {
// TODO check from Capability
if tr.Reference.Kind == "Route" && tr.Reference.APIVersion == "standard.oam.dev/v1alpha1" {
return tr.Reference.Name
}
}
}
return ""
}
func (o *VelaPortForwardOptions) Complete() error {
svcName, err := util.AskToChooseOneService(o.App.GetComponents())
if err != nil {
return err
}
if o.routeTrait {
appconfig, err := application.GetAppConfig(o.Context, o.Client, o.App, o.Env)
if err != nil {
return err
}
routeSvc := GetRouteServiceName(appconfig, svcName)
if routeSvc == "" {
return fmt.Errorf("no route trait found in %s %s", o.App.Name, svcName)
}
var svc = corev1.Service{}
err = o.Client.Get(o.Context, types2.NamespacedName{Name: routeSvc, Namespace: o.Env.Namespace}, &svc)
if err != nil {
return err
}
if len(svc.Spec.Ports) <= 0 {
return fmt.Errorf("no port found in service %s", routeSvc)
}
val := strconv.Itoa(int(svc.Spec.Ports[0].Port))
if val == "80" {
val = "8080:80"
} else if val == "443" {
val = "8443:443"
}
o.Args = append(o.Args, val)
args := make([]string, len(o.Args))
copy(args, o.Args)
args[0] = "svc/" + routeSvc
return o.kcPortForwardOptions.Complete(o.f, o.Cmd, args)
}
podName, err := o.getPodName(svcName)
if err != nil {
return err

View File

@@ -44,6 +44,7 @@ func TestPortForwardCommand(t *testing.T) {
kcPortForwardOptions: &portforward.PortForwardOptions{},
f: tf,
ClientSet: fakeClientSet,
VelaC: fakeC,
}
err := o.Init(context.Background(), cmd, []string{"fakeApp", "8081:8080"})
assert.NoError(t, err)

View File

@@ -403,8 +403,8 @@ func getApp(ctx context.Context, c client.Client, compName, appName string, env
return nil, nil, err
}
appConfig := &v1alpha2.ApplicationConfiguration{}
if err = c.Get(ctx, client.ObjectKey{Namespace: env.Namespace, Name: app.Name}, appConfig); err != nil {
appConfig, err := application.GetAppConfig(ctx, c, app, env)
if err != nil {
return nil, nil, err
}
return app, appConfig, nil

View File

@@ -22,6 +22,8 @@ import (
"fmt"
"os"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/go-logr/logr"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
@@ -55,12 +57,12 @@ func init() {
// Note: reconsider delegating the work to helm operator.
func Install(kubecli client.Client) {
velaConfig, err := fetchVelaConfig(kubecli)
if err != nil {
log.Error(err, "fetchVelaConfig failed")
if apierrors.IsNotFound(err) {
log.Info("no ConfigMap('vela-config') found in vela-system namespace, will not install any dependency")
return
}
if velaConfig == nil {
log.Info("no vela config")
if err != nil {
log.Error(err, "fetch ConfigMap('vela-config') in vela-system namespace failed")
return
}
for key, chart := range velaConfig.Data {
@@ -96,7 +98,7 @@ func fetchVelaConfig(kubecli client.Client) (*v1.ConfigMap, error) {
velaConfigNN := k8stypes.NamespacedName{Name: VelaConfigName, Namespace: types.DefaultOAMNS}
velaConfig := &v1.ConfigMap{}
if err := kubecli.Get(context.TODO(), velaConfigNN, velaConfig); err != nil {
return nil, client.IgnoreNotFound(err)
return nil, err
}
return velaConfig, nil
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"strconv"
"strings"
standardv1alpha1 "github.com/oam-dev/kubevela/api/v1alpha1"
@@ -100,6 +101,10 @@ func (n *Nginx) CheckStatus(routeTrait *standardv1alpha1.Route) (string, []runti
func (*Nginx) Construct(routeTrait *standardv1alpha1.Route) []*v1beta1.Ingress {
// Don't create ingress if no host set, this is used for local K8s cluster demo and the route trait will create K8s service only.
if routeTrait.Spec.Host == "" || strings.Contains(routeTrait.Spec.Host, "localhost") || strings.Contains(routeTrait.Spec.Host, "127.0.0.1") {
return nil
}
var ingresses []*v1beta1.Ingress
for idx, rule := range routeTrait.Spec.Rules {
name := rule.Name

View File

@@ -125,7 +125,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
oamutil.PatchCondition(ctx, r, &routeTrait,
cpv1alpha1.ReconcileError(errors.Wrap(err, errApplyNginxIngress)))
}
r.record.Event(eventObj, event.Normal("nginx ingress created",
r.record.Event(eventObj, event.Normal("nginx ingress patched",
fmt.Sprintf("successfully server side patched a route trait `%s`", routeTrait.Name)))
}
// TODO(wonderflow): GC mechanism for no used ingress, service, issuer

View File

@@ -132,6 +132,9 @@ func (d *RouteChecker) Check(ctx context.Context, reference runtimev1alpha1.Type
}
message += fmt.Sprintf("\tVisiting URL: %s\tIP: %s\n", url, addr)
}
if len(route.Status.Ingresses) == 0 {
message += fmt.Sprintf("Visiting by using 'vela port-forward %s --route'\n", appConfig.Name)
}
return StatusDone, message, nil
}

View File

@@ -63,7 +63,7 @@ func GetWorkloadsFromCluster(ctx context.Context, namespace string, c types.Args
}
gvk, err := util.GetGVKFromDefinition(dm, wd.Spec.Reference)
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("make sure you have installed CRD(controller) for this capability '%s': %v ", wd.Name, err)
}
tmp.CrdInfo = &types.CrdInfo{
APIVersion: gvk.GroupVersion().String(),
@@ -99,7 +99,7 @@ func GetTraitsFromCluster(ctx context.Context, namespace string, c types.Args, s
}
gvk, err := util.GetGVKFromDefinition(dm, td.Spec.Reference)
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("make sure you have installed CRD(controller) for this capability '%s': %v ", td.Name, err)
}
tmp.CrdInfo = &types.CrdInfo{
APIVersion: gvk.GroupVersion().String(),