Compare commits

..

11 Commits

Author SHA1 Message Date
Sun Jianbo
525c228bd7 Merge pull request #311 from wonderflow/fix
fix env exist but issuer not exist
2020-09-23 11:22:11 +08:00
天元
ba6a53c6f5 fix env exist but issuer not exist 2020-09-23 10:38:59 +08:00
Sun Jianbo
0d1dd62b8a Merge pull request #308 from wonderflow/init
vela init to create and run application in one command
2020-09-22 20:23:41 +08:00
天元
5ac7265474 vela init to create and run application in one command 2020-09-22 20:07:53 +08:00
Zheng Xi Zhou
08701568a1 Fix OpenAPIV3Schema Validation Issue (#300)
* Fix OpenAPIV3Schema Validation Issue

Temporarily corrects spec.validation.openAPIV3Schema issue, and it would be removed
after this issue was fixed https://github.com/oam-dev/kubevela/issues/284.

* fix vela install issue

* return error during fixing APIV3SchemaValidation issue
2020-09-22 15:51:38 +08:00
roy wang
faedce906a fix #128 | sync deleted workload locally
Signed-off-by: roy wang <seiwy2010@gmail.com>

fix & add unit tests

Signed-off-by: roy wang <seiwy2010@gmail.com>

fix info output & unit test

Signed-off-by: roy wang <seiwy2010@gmail.com>

fix e2e-test

Signed-off-by: roy wang <seiwy2010@gmail.com>
2020-09-21 19:22:34 +08:00
Sun Jianbo
f5908741d3 Update pkg/commands/system.go 2020-09-21 19:22:34 +08:00
mosesyou
5414cc1f86 fix duplicate vela system info failure message 2020-09-21 19:22:34 +08:00
天元
371a989f9b add move readme 2020-09-21 19:22:34 +08:00
天元
8b3af3be93 temporarily add containerized to chart 2020-09-21 19:22:34 +08:00
hanxie
d6e519f1c4 Components static page,bugfix,capability delete 2020-09-21 19:22:34 +08:00
16 changed files with 729 additions and 58 deletions

3
.gitignore vendored
View File

@@ -46,3 +46,6 @@ dashboard/package-lock.json
dashboard/src/.umi/
package-lock.json
dashboard/src/.umi-production/
# build
charts/vela

251
README.md
View File

@@ -24,59 +24,254 @@ sudo mv ./vela /usr/local/bin/vela
```shell script
$ vela install
```
This command will install vela core controller into your K8s cluster, along with built-in workloads and traits.
## Demos
* Create ENV
After `vela install` you will have workloads and traits locally, and available to use by vela cli.
```shell script
vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
$ vela workloads
NAME DEFINITION
backend containerizeds.standard.oam.dev
containerized containerizedworkloads.core.oam.dev
task jobs
webservice containerizeds.standard.oam.dev
```
* Create Component
```shell script
$ vela traits
NAME DEFINITION APPLIES TO
route routes.standard.oam.dev webservice
backend
scale manualscalertraits.core.oam.dev webservice
backend
```
For example, use the following command to create and run an application.
### Create ENV
Before working with your application, you should create an env for it.
```shell script
$ vela comp run mycomp -t webservice --image crccheck/hello-world --port 8000
$ vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
ENV myenv CREATED, Namespace: myenv, Email: my@email.com.
```
It will create a namespace called myenv
```shell script
$ kubectl get ns
NAME STATUS AGE
myenv Active 40s
```
A namespace level issuer for certificate generation with email.
```shell script
$ kubectl get issuers.cert-manager.io -n myenv
NAME READY AGE
oam-env-myenv True 40s
```
A env metadata in your local:
```shell script
$ cat ~/.vela/envs/myenv/config.json
{"name":"myenv","namespace":"myenv","email":"my@email.com","domain":"kubevela.io","issuer":"oam-env-myenv"}
```
### Create Component
Then let's create application, we will use our env created by default.
```shell script
$ vela comp run mycomp -t webservice --image crccheck/hello-world --port 8000 --app myapp
Creating AppConfig appcomp
SUCCEED
```
* Add Trait
It will create component named `mycomp`.
```shell script
$ vela route mycomp
Adding route for app abc
Succeeded!
$ kubectl get components -n myenv
NAME WORKLOAD-KIND AGE
mycomp Containerized 10s
```
* Check Status
And an AppConfig named myapp.
```
$ vela comp status abc
Showing status of Component abc deployed in Environment t2
Component Status:
Name: abc Containerized(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind Containerized workload is unknown for HealthScope
Traits
└─Trait/route
Last Deployment:
Created at: 2020-09-18 18:47:09 +0800 CST
Updated at: 2020-09-18T18:47:16+08:00
```shell script
$ kubectl get appconfig -n myenv
NAME AGE
myapp 24s
```
* Delete App
Vela Core will work for AppConfig and create K8s deployment and service.
```shell script
$ kubectl get deployment -n myenv
NAME READY UP-TO-DATE AVAILABLE AGE
mycomp 1/1 1 1 38s
```
```shell script
$ kubectl get svc -n myenv
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mycomp ClusterIP 172.21.4.228 <none> 8080/TCP 49s
```
### Multiple Component
Creating a new component in the same application is easy, just use the `--app` flag.
```shell script
$ vela comp run db -t backend --image crccheck/hello-world --app myapp
Creating App myapp
SUCCEED
```
```shell script
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
db myapp backend Deployed 2020-09-18 22:42:04 +0800 CST
mycomp myapp webservice Deployed 2020-09-18 22:42:04 +0800 CST
```
Now we can see the application deployed, let's add route trait for visiting.
### Add Trait
```shell script
$ vela route mycomp --app myapp
Adding route for app mycomp
Succeeded!
```
It will create route trait for this component.
```shell script
$ kubeclt get routes.standard.oam.dev -n myenv
NAME AGE
mycomp-trait-5b576c4fc 18s
```
Controller of route trait which is part of vela core will create an ingress for it.
```shell script
$ kubectl get ingress -n myenv
NAME HOSTS ADDRESS PORTS AGE
mycomp-trait-5b576c4fc mycomp.kubevela.io 123.57.10.233 80, 443 73s
```
Please configure your domain pointing to the public address.
Then you will be able to visit it by `https://mycomp.kubevela.io`, `mTLS` is automatically enabled.
### Check Status
App level:
```shell script
$ vela app show myapp
About:
Name: myapp
Created at: 2020-09-18 22:42:04.191171 +0800 CST
Updated at: 2020-09-18 22:51:11.128997 +0800 CST
Environment:
Namespace: myenv
Components:
Name Type Traits
db backend
mycomp webservice route
```
Component Level:
```shell script
$ vela comp show mycomp
About:
Name: mycomp
WorkloadType: webservice
Application: myapp
Environment:
Namespace: myenv
Arguments:
image: crccheck/hello-world
name: mycomp
port: 8000
Traits:
route:
domain: mycomp.kubevela.io
issuer: oam-env-myenv
name: route
```
```
$ vela comp status mycomp
Showing status of Component mycomp deployed in Environment myenv
Component Status:
Name: mycomp Containerized(type) UNKNOWN APIVersion standard.oam.dev/v1alpha1 Kind Containerized workload is unknown for HealthScope
Traits
└─Trait/route
Last Deployment:
Created at: 2020-09-18 22:42:04 +0800 CST
Updated at: 2020-09-18T22:51:11+08:00
```
### Delete App or Component
```shell script
$ vela app ls
abc
myapp
```
$ vela app delete abc
Deleting Application "abc"
delete apps succeed abc from t2
```shell script
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
db myapp backend Deployed 2020-09-18 22:42:04 +0800 CST
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
```
```shell script
$ vela comp delete db
Deleting Component 'db' from Application 'db'
```
```shell script
$ vela comp ls
NAME APP WORKLOAD TRAITS STATUS CREATED-TIME
mycomp myapp webservice route Deployed 2020-09-18 22:42:04 +0800 CST
```
```shell script
$ vela app delete myapp
Deleting Application "myapp"
delete apps succeed myapp from myenv
```
## Dashboard
We also prepared a dashboard for you, but it's still in heavily development.
```shell script
$ vela dashboard
```
#### Auto-Completion

View File

@@ -11,8 +11,6 @@ spec:
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
- apiVersion: v1
kind: Service
extension:
template: |
#Template: {
@@ -21,16 +19,19 @@ spec:
metadata:
name: backend.name
spec: {
containers: [{
image: backend.image
name: backend.name
}]
replicas: 1
podSpec: {
containers: [{
image: backend.image
name: backend.name
}]
}
}
}
backend: {
name: string
// +usage=specify app image
// +short=i
image: string
}

View File

@@ -98,6 +98,7 @@ func newCommand() *cobra.Command {
// Getting Start
NewVersionCommand(),
commands.NewInitCommand(commandArgs, ioStream),
// Apps
commands.NewAppsCommand(commandArgs, ioStream),

View File

@@ -46,7 +46,9 @@ var (
output, err := Exec("vela system update")
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(output).To(gomega.ContainSubstring("syncing workload definitions from cluster..."))
gomega.Expect(output).To(gomega.ContainSubstring("successfully synced"))
gomega.Expect(output).To(gomega.ContainSubstring("sync"))
gomega.Expect(output).To(gomega.ContainSubstring("successfully"))
gomega.Expect(output).To(gomega.ContainSubstring("remove"))
})
})
}

1
go.mod
View File

@@ -4,6 +4,7 @@ go 1.13
require (
cuelang.org/go v0.2.2
github.com/AlecAivazis/survey/v2 v2.1.1
github.com/Azure/go-autorest v12.2.0+incompatible // Don't remove. https://github.com/kubernetes/client-go/issues/628
github.com/coreos/prometheus-operator v0.41.1
github.com/crossplane/crossplane-runtime v0.9.0

12
go.sum
View File

@@ -36,6 +36,9 @@ contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeS
cuelang.org/go v0.2.2 h1:i/wFo48WDibGHKQTRZ08nB8PqmGpVpQ2sRflZPj73nQ=
cuelang.org/go v0.2.2/go.mod h1:Dyjk8Y/B3CfFT1jQKJU0g5PpCeMiDe0yMOhk57oXwqo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AlecAivazis/survey v1.8.8 h1:Y4yypp763E8cbqb5RBqZhGgkCFLRFnbRBHrxnpMMsgQ=
github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI=
github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
github.com/AlekSi/gocov-xml v0.0.0-20190121064608-3a14fb1c4737/go.mod h1:w1KSuh2JgIL3nyRiZijboSUwbbxOrTzWwyWVFUHtXBQ=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
@@ -119,6 +122,7 @@ github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZ
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
@@ -781,6 +785,7 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -844,6 +849,8 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -873,6 +880,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -956,6 +964,8 @@ github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4f
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archiver/v3 v3.3.0 h1:vWjhY8SQp5yzM9P6OJ/eZEkmi3UAbRrxCq48MxjAzig=
github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
github.com/miekg/dns v0.0.0-20170721150254-0f3adef2e220/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -1296,6 +1306,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
@@ -1578,6 +1589,7 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -1,9 +1,13 @@
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/openservicemesh/osm/pkg/cli"
)
@@ -11,10 +15,102 @@ import (
func main() {
// Path relative to the Makefile where this is invoked.
chartPath := filepath.Join("charts", "vela-core")
source, err := cli.GetChartSource(chartPath)
tempChartPath, err := fixOpenAPIV3SchemaValidationIssue(chartPath)
if err != nil {
fmt.Fprintln(os.Stderr, "fixing OpenAPIV3SchemaValidation issue hit an error:", err)
os.Exit(1)
}
source, err := cli.GetChartSource(tempChartPath)
if err != nil {
fmt.Fprintln(os.Stderr, "error getting chart source:", err)
os.Exit(1)
}
fmt.Print(source)
// Delete the temporary Chart path
os.RemoveAll(tempChartPath)
}
// fixOpenAPIV3SchemaValidationIssue temporarily corrects spec.validation.openAPIV3Schema issue, and it would be removed
// after this issue was fixed https://github.com/oam-dev/kubevela/issues/284.
func fixOpenAPIV3SchemaValidationIssue(chartPath string) (string, error) {
newDir, err := ioutil.TempDir(".", "charts")
if err != nil {
fmt.Fprintln(os.Stderr, "failed to crate temporary directory:", err)
return "", err
}
err = filepath.Walk(chartPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Fprintln(os.Stderr, "failed to list the content of", path)
return err
}
targetPath := filepath.Join(newDir, path)
if info.IsDir() {
if err = os.MkdirAll(targetPath, os.ModePerm); err != nil {
fmt.Fprintln(os.Stderr, "failed to make dir for", targetPath)
}
} else {
targetFile, err := os.Create(filepath.Join(newDir, path))
if err != nil {
fmt.Fprintln(os.Stderr, "failed to open file:", path)
return err
}
defer targetFile.Close()
if strings.Contains(path, filepath.Join(chartPath, "crds")) && info.Name() == "standard.oam.dev_containerizeds.yaml" {
f, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to open file", path)
return err
}
defer f.Close()
r := bufio.NewReader(f)
var previousLine string
if previousLine, err = r.ReadString('\n'); err != nil {
fmt.Fprintln(os.Stderr, "failed to read file line:", err)
return err
}
fmt.Fprint(targetFile, previousLine)
for {
line, err := r.ReadString('\n')
if err != nil {
if err == io.EOF {
return nil
}
fmt.Fprintln(os.Stderr, "failed to read file line:", err)
return err
}
if strings.Contains(previousLine, "protocol:") &&
strings.Contains(line, "description: Protocol for port. Must be UDP, TCP,") {
tmp := strings.Split(line, "description")
if len(tmp) > 0 {
blanks := tmp[0]
defaultStr := blanks + "default: TCP\n"
fmt.Fprint(targetFile, defaultStr)
}
}
fmt.Fprint(targetFile, line)
previousLine = line
}
} else {
data, err := ioutil.ReadFile(path)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to read file", err)
return err
}
if err = ioutil.WriteFile(targetPath, data, os.ModePerm); err != nil {
fmt.Fprintln(os.Stderr, "failed to read file:", err)
return err
}
}
}
return nil
})
if err != nil {
return "", err
}
return filepath.Join(newDir, "charts", "vela-core"), nil
}

View File

@@ -116,7 +116,7 @@ func (o *runOptions) Complete(cmd *cobra.Command, args []string) error {
return err
}
appGroup, err := flags.GetString(App)
appName, err := flags.GetString(App)
if err != nil {
return err
}
@@ -153,7 +153,7 @@ func (o *runOptions) Complete(cmd *cobra.Command, args []string) error {
if err = flags.Parse(args); err != nil {
return err
}
app, err := oam.BaseComplete(envName, workloadName, appGroup, flags, workloadType)
app, err := oam.BaseComplete(envName, workloadName, appName, flags, workloadType)
if err != nil {
return err
}

276
pkg/commands/init.go Normal file
View File

@@ -0,0 +1,276 @@
package commands
import (
"context"
"fmt"
"strconv"
"cuelang.org/go/cue"
"github.com/AlecAivazis/survey/v2"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/api/types"
"github.com/oam-dev/kubevela/pkg/application"
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/plugins"
)
type appInitOptions struct {
client client.Client
cmdutil.IOStreams
Env *types.EnvMeta
app *application.Application
appName string
workloadName string
workloadType string
}
// NewInitCommand init application
func NewInitCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
o := &appInitOptions{IOStreams: ioStreams}
cmd := &cobra.Command{
Use: "init",
DisableFlagsInUseLine: true,
Short: "Init an OAM Application",
Long: "Init an OAM Application by one command",
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
Example: "vela init",
RunE: func(cmd *cobra.Command, args []string) error {
newClient, err := client.New(c.Config, client.Options{Scheme: c.Schema})
if err != nil {
return err
}
o.client = newClient
o.Env, err = GetEnv(cmd)
if err != nil {
return err
}
o.IOStreams.Info("Welcome to use KubeVela CLI! We're going to help you run applications through a couple of questions.")
o.IOStreams.Info()
if err = o.CheckEnv(); err != nil {
return err
}
if err = o.Naming(); err != nil {
return err
}
if err = o.Workload(); err != nil {
return err
}
if err = o.Traits(); err != nil {
return err
}
_, err = oam.BaseRun(false, o.app, o.client, o.Env)
if err != nil {
return err
}
o.IOStreams.Info("App Deployed Succeed")
//TODO(wonderflow) Wait for app running, and print trait info such as route, domain
return printComponentStatus(context.Background(), o.client, o.IOStreams, o.workloadName, o.appName, o.Env)
},
}
cmd.SetOut(ioStreams.Out)
return cmd
}
func (o *appInitOptions) Naming() error {
prompt := &survey.Input{
Message: "What would you like to name your application: ",
}
err := survey.AskOne(prompt, &o.appName)
if err != nil {
return fmt.Errorf("read app name err %v", err)
}
return nil
}
func (o *appInitOptions) CheckEnv() error {
if o.Env.Namespace == "" {
o.Env.Namespace = "default"
}
o.Infof("Environment: %s, namespace: %s\n\n", o.Env.Name, o.Env.Namespace)
if o.Env.Domain == "" {
prompt := &survey.Input{
Message: "Do you want to setup a domain for web service: ",
}
err := survey.AskOne(prompt, &o.Env.Domain)
if err != nil {
return fmt.Errorf("read app name err %v", err)
}
}
if o.Env.Email == "" {
prompt := &survey.Input{
Message: "Provide an email for production certification: ",
}
err := survey.AskOne(prompt, &o.Env.Email)
if err != nil {
return fmt.Errorf("read app name err %v", err)
}
}
if _, err := oam.CreateOrUpdateEnv(context.Background(), o.client, o.Env.Name, o.Env); err != nil {
return err
}
return nil
}
func (o *appInitOptions) Workload() error {
workloads, err := plugins.LoadInstalledCapabilityWithType(types.TypeWorkload)
if err != nil {
return err
}
var workloadList []string
for _, w := range workloads {
workloadList = append(workloadList, w.Name)
}
prompt := &survey.Select{
Message: "Choose an workload for your component: ",
Options: workloadList,
}
err = survey.AskOne(prompt, &o.workloadType)
if err != nil {
return fmt.Errorf("read workload type err %v", err)
}
workload, err := GetCapabilityByName(o.workloadType, workloads)
if err != nil {
return err
}
namePrompt := &survey.Input{
Message: fmt.Sprintf("What would you name this %s: ", o.workloadType),
}
err = survey.AskOne(namePrompt, &o.workloadName)
if err != nil {
return fmt.Errorf("read name err %v", err)
}
fs := pflag.NewFlagSet("workload", pflag.ContinueOnError)
for _, p := range workload.Parameters {
if p.Name == "name" {
continue
}
usage := p.Usage
if usage == "" {
usage = "what would you configure for parameter '" + color.New(color.FgCyan).Sprintf("%s", p.Name) + "'"
}
switch p.Type {
case cue.StringKind:
var data string
prompt := &survey.Input{
Message: usage,
}
var opts []survey.AskOpt
if p.Required {
opts = append(opts, survey.WithValidator(survey.Required))
}
err = survey.AskOne(prompt, &data, opts...)
if err != nil {
return fmt.Errorf("read param %s err %v", p.Name, err)
}
fs.String(p.Name, data, p.Usage)
case cue.NumberKind, cue.FloatKind:
var data string
prompt := &survey.Input{
Message: usage,
}
var opts []survey.AskOpt
if p.Required {
opts = append(opts, survey.WithValidator(survey.Required))
}
opts = append(opts, survey.WithValidator(func(ans interface{}) error {
data := ans.(string)
if data == "" && !p.Required {
return nil
}
_, err := strconv.ParseFloat(data, 64)
return err
}))
err = survey.AskOne(prompt, &data, opts...)
if err != nil {
return fmt.Errorf("read param %s err %v", p.Name, err)
}
val, _ := strconv.ParseFloat(data, 64)
fs.Float64(p.Name, val, p.Usage)
case cue.IntKind:
var data string
prompt := &survey.Input{
Message: usage,
}
var opts []survey.AskOpt
if p.Required {
opts = append(opts, survey.WithValidator(survey.Required))
}
opts = append(opts, survey.WithValidator(func(ans interface{}) error {
data := ans.(string)
if data == "" && !p.Required {
return nil
}
_, err := strconv.ParseInt(data, 10, 64)
return err
}))
err = survey.AskOne(prompt, &data, opts...)
if err != nil {
return fmt.Errorf("read param %s err %v", p.Name, err)
}
val, _ := strconv.ParseInt(data, 10, 64)
fs.Int64(p.Name, val, p.Usage)
case cue.BoolKind:
var data bool
prompt := &survey.Confirm{
Message: usage,
}
if p.Required {
err = survey.AskOne(prompt, &data, survey.WithValidator(survey.Required))
} else {
err = survey.AskOne(prompt, &data)
}
if err != nil {
return fmt.Errorf("read param %s err %v", p.Name, err)
}
fs.Bool(p.Name, data, p.Usage)
}
}
o.app, err = oam.BaseComplete(o.Env.Name, o.workloadName, o.appName, fs, o.workloadType)
return err
}
func GetCapabilityByName(name string, workloads []types.Capability) (types.Capability, error) {
for _, v := range workloads {
if v.Name == name {
return v, nil
}
}
return types.Capability{}, fmt.Errorf("%s not found", name)
}
func (o *appInitOptions) Traits() error {
traits, err := plugins.LoadInstalledCapabilityWithType(types.TypeTrait)
if err != nil {
return err
}
switch o.workloadType {
case "webservice":
//TODO(wonderflow) this should get from workload definition to know which trait should be suggestions
var suggestTraits = []string{"route"}
for _, tr := range suggestTraits {
trait, err := GetCapabilityByName(tr, traits)
if err != nil {
continue
}
tflags := pflag.NewFlagSet("trait", pflag.ContinueOnError)
for _, pa := range trait.Parameters {
types.SetFlagBy(tflags, pa)
}
//TODO(wonderflow): give a way to add parameter for trait
o.app, err = oam.AddOrUpdateTrait(o.Env, o.appName, o.workloadName, tflags, trait)
if err != nil {
return err
}
}
return nil
}
return nil
}

View File

@@ -60,6 +60,7 @@ func NewCompListCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Comman
ctx := context.Background()
cmd := &cobra.Command{
Use: "ls",
Aliases: []string{"list"},
DisableFlagsInUseLine: true,
Short: "List applications",
Long: "List applications with workloads, traits, status and created time",

View File

@@ -38,22 +38,28 @@ func NewRefreshCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command
func RefreshDefinitions(ctx context.Context, c client.Client, ioStreams cmdutil.IOStreams) error {
dir, _ := system.GetCapabilityDir()
syncedTemplates := []types.Capability{}
ioStreams.Info("syncing workload definitions from cluster...")
templates, err := plugins.GetWorkloadsFromCluster(ctx, types.DefaultOAMNS, c, dir, nil)
if err != nil {
return err
}
ioStreams.Infof("get %d workload definitions from cluster, syncing...", len(templates))
syncedTemplates = append(syncedTemplates, templates...)
ioStreams.Infof("get %d workload definition(s) from cluster, syncing...", len(templates))
successNum := plugins.SinkTemp2Local(templates, dir)
ioStreams.Infof("%d workload definitions successfully synced\n", successNum)
ioStreams.Infof("sync %d workload definition(s) successfully\n", successNum)
ioStreams.Info("syncing trait definitions from cluster...")
templates, err = plugins.GetTraitsFromCluster(ctx, types.DefaultOAMNS, c, dir, nil)
if err != nil {
return err
}
ioStreams.Infof("get %d trait definitions from cluster, syncing...", len(templates))
syncedTemplates = append(syncedTemplates, templates...)
ioStreams.Infof("get %d trait definition(s) from cluster, syncing...", len(templates))
successNum = plugins.SinkTemp2Local(templates, dir)
ioStreams.Infof("%d trait definitions successfully synced\n", successNum)
ioStreams.Infof("sync %d trait definition(s) successfully\n", successNum)
legacyNum := plugins.RemoveLegacyTemps(syncedTemplates, dir)
ioStreams.Infof("remove %d legacy capability definition(s) successfully\n", legacyNum)
return nil
}

View File

@@ -87,8 +87,7 @@ func NewAdminInfoCommand(ioStreams cmdutil.IOStreams) *cobra.Command {
func (i *infoCmd) run(ioStreams cmdutil.IOStreams) error {
clusterVersion, err := GetOAMReleaseVersion()
if err != nil {
ioStreams.Errorf("fail to get cluster chartPath, err: %v \n", err)
return err
return fmt.Errorf("fail to get cluster chartPath: %v", err)
}
ioStreams.Info("Versions:")
ioStreams.Infof("oam-kubernetes-runtime: %s \n", clusterVersion)

View File

@@ -139,6 +139,41 @@ func SinkTemp2Local(templates []types.Capability, dir string) int {
return success
}
// RemoveLegacyTemps will remove capability definitions under `dir` but not included in `retainedTemps`.
func RemoveLegacyTemps(retainedTemps []types.Capability, dir string) int {
success := 0
retainedFiles := []string{}
subDirs := []string{GetSubDir(dir, types.TypeWorkload), GetSubDir(dir, types.TypeTrait)}
for _, tmp := range retainedTemps {
subDir := GetSubDir(dir, tmp.Type)
tmpFilePath := filepath.Join(subDir, tmp.Name)
retainedFiles = append(retainedFiles, tmpFilePath)
}
for _, subDir := range subDirs {
if err := filepath.Walk(subDir, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() {
// omit subDir or subDir not exist
return nil
}
for _, retainedFile := range retainedFiles {
if retainedFile == path {
return nil
}
}
if err := os.Remove(path); err != nil {
fmt.Printf("remove legacy %s err: %v\n", path, err)
return err
}
success++
return nil
}); err != nil {
continue
}
}
return success
}
func LoadCapabilityFromSyncedCenter(dir string) ([]types.Capability, error) {
var tmps []types.Capability
files, err := ioutil.ReadDir(dir)

View File

@@ -9,8 +9,8 @@ import (
"github.com/stretchr/testify/assert"
)
func TestLocalSink(t *testing.T) {
deployment := types.Capability{
var (
deployment = types.Capability{
Name: "deployment",
Type: types.TypeWorkload,
Parameters: []types.Parameter{
@@ -21,7 +21,7 @@ func TestLocalSink(t *testing.T) {
},
},
}
statefulset := types.Capability{
statefulset = types.Capability{
Name: "statefulset",
Type: types.TypeWorkload,
Parameters: []types.Parameter{
@@ -32,7 +32,7 @@ func TestLocalSink(t *testing.T) {
},
},
}
route := types.Capability{
route = types.Capability{
Name: "route",
Type: types.TypeTrait,
Parameters: []types.Parameter{
@@ -43,6 +43,9 @@ func TestLocalSink(t *testing.T) {
},
},
}
)
func TestLocalSink(t *testing.T) {
cases := map[string]struct {
dir string
@@ -103,3 +106,43 @@ func testInDir(t *testing.T, casename, dir string, tmps, defexp []types.Capabili
assert.Equal(t, defexp, gotDef, casename)
}
}
func TestRemoveLegacyTemps(t *testing.T) {
cases := []struct {
caseName string
newTemps []types.Capability
rmNum int
}{
{
caseName: "remove all",
newTemps: []types.Capability{},
rmNum: 3,
},
{
caseName: "nothing removed",
newTemps: []types.Capability{deployment, statefulset, route},
rmNum: 0,
},
{
caseName: "remove part of existings",
newTemps: []types.Capability{statefulset, route},
rmNum: 1,
},
}
for _, c := range cases {
runInDirRemoveLegacyTemps(t, c.caseName, c.newTemps, c.rmNum)
}
}
func runInDirRemoveLegacyTemps(t *testing.T, caseName string, newTemps []types.Capability, rmNum int) {
dir := "vela-test-rm-temps"
err := os.MkdirAll(dir, 0755)
assert.NoError(t, err, caseName)
defer os.RemoveAll(dir)
existingTemps := []types.Capability{deployment, statefulset, route}
number := SinkTemp2Local(existingTemps, dir)
assert.Equal(t, 3, number)
resultRemoveNum := RemoveLegacyTemps(newTemps, dir)
assert.Equal(t, rmNum, resultRemoveNum, caseName)
}

View File

@@ -1,13 +1,16 @@
#Template: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ContainerizedWorkload"
apiVersion: "standard.oam.dev/v1alpha1"
kind: "Containerized"
metadata:
name: backend.name
spec: {
containers: [{
image: backend.image
name: backend.name
}]
replicas: 1
podSpec: {
containers: [{
image: backend.image
name: backend.name
}]
}
}
}
@@ -16,7 +19,4 @@ backend: {
// +usage=specify app image
// +short=i
image: string
// +usage=specify port for container
// +short=p
port: *6379 | int
}