Feat: add email support in webhook notification (#2535)

* Feat: add email support in webhook notification

* Fix: change sender and receiver to from and to

* fix the variable name

* fix wait return
This commit is contained in:
Tianxin Dong
2021-10-28 10:59:17 +08:00
committed by GitHub
parent 7e5dac7c98
commit 828acd95fa
14 changed files with 629 additions and 101 deletions

View File

@@ -18,13 +18,7 @@ spec:
parameter: {
dingding?: {
url: {
address?: string
fromSecret?: {
name: string
key: string
}
}
url: value | secretRef
message: {
text?: *null | {
content: string
@@ -69,13 +63,7 @@ spec:
}
slack?: {
url: {
address?: string
fromSecret?: {
name: string
key: string
}
}
url: value | secretRef
message: {
text: string
blocks?: *null | [...block]
@@ -87,6 +75,21 @@ spec:
mrkdwn?: *true | bool
}
}
email?: {
from: {
address: string
alias?: string
password: value | secretRef
host: string
port: *587 | int
}
to: [...string]
content: {
subject: string
body: string
}
}
}
block: {
type: string
@@ -133,28 +136,33 @@ spec:
description?: text
url?: string
}
secretRef: {
name: string
key: string
}
value: string
// send webhook notification
ding: op.#Steps & {
if parameter.dingding != _|_ {
if parameter.dingding.url.address != _|_ {
if parameter.dingding.url.value != _|_ {
ding1: op.#DingTalk & {
message: parameter.dingding.message
dingUrl: parameter.dingding.url.address
dingUrl: parameter.dingding.url.value
}
}
if parameter.dingding.url.fromSecret != _|_ && parameter.dingding.url.address == _|_ {
if parameter.dingding.url.secretRef != _|_ && parameter.dingding.url.value == _|_ {
read: op.#Read & {
value: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: parameter.dingding.url.fromSecret.name
name: parameter.dingding.url.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.dingding.url.fromSecret.key])
decoded: base64.Decode(null, read.value.data[parameter.dingding.url.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
ding2: op.#DingTalk & {
message: parameter.dingding.message
@@ -165,25 +173,25 @@ spec:
}
slack: op.#Steps & {
if parameter.slack != _|_ {
if parameter.slack.url.address != _|_ {
if parameter.slack.url.value != _|_ {
slack1: op.#Slack & {
message: parameter.slack.message
slackUrl: parameter.slack.url.address
slackUrl: parameter.slack.url.value
}
}
if parameter.slack.url.fromSecret != _|_ && parameter.slack.url.address == _|_ {
if parameter.slack.url.secretRef != _|_ && parameter.slack.url.value == _|_ {
read: op.#Read & {
value: {
kind: "Secret"
apiVersion: "v1"
metadata: {
name: parameter.slack.url.fromSecret.name
name: parameter.slack.url.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.slack.url.fromSecret.key])
decoded: base64.Decode(null, read.value.data[parameter.slack.url.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
slack2: op.#Slack & {
message: parameter.slack.message
@@ -192,4 +200,48 @@ spec:
}
}
}
email: op.#Steps & {
if parameter.email != _|_ {
if parameter.email.from.password.value != _|_ {
email1: op.#SendEmail & {
from: {
address: parameter.email.from.value
alias: parameter.email.from.alias
password: parameter.email.from.password.value
host: parameter.email.from.host
port: parameter.email.from.port
}
to: parameter.email.to
content: parameter.email.content
}
}
if parameter.email.from.password.secretRef != _|_ && parameter.email.from.password.value == _|_ {
read: op.#Read & {
value: {
kind: "Secret"
apiVersion: "v1"
metadata: {
name: parameter.email.from.password.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.email.from.password.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
email2: op.#SendEmail & {
from: {
address: parameter.email.from.value
alias: parameter.email.from.alias
password: stringValue.str
host: parameter.email.from.host
port: parameter.email.from.port
}
to: parameter.email.to
content: parameter.email.content
}
}
}
}

View File

@@ -18,13 +18,7 @@ spec:
parameter: {
dingding?: {
url: {
address?: string
fromSecret?: {
name: string
key: string
}
}
url: value | secretRef
message: {
text?: *null | {
content: string
@@ -69,13 +63,7 @@ spec:
}
slack?: {
url: {
address?: string
fromSecret?: {
name: string
key: string
}
}
url: value | secretRef
message: {
text: string
blocks?: *null | [...block]
@@ -87,6 +75,21 @@ spec:
mrkdwn?: *true | bool
}
}
email?: {
from: {
address: string
alias?: string
password: value | secretRef
host: string
port: *587 | int
}
to: [...string]
content: {
subject: string
body: string
}
}
}
block: {
type: string
@@ -133,28 +136,33 @@ spec:
description?: text
url?: string
}
secretRef: {
name: string
key: string
}
value: string
// send webhook notification
ding: op.#Steps & {
if parameter.dingding != _|_ {
if parameter.dingding.url.address != _|_ {
if parameter.dingding.url.value != _|_ {
ding1: op.#DingTalk & {
message: parameter.dingding.message
dingUrl: parameter.dingding.url.address
dingUrl: parameter.dingding.url.value
}
}
if parameter.dingding.url.fromSecret != _|_ && parameter.dingding.url.address == _|_ {
if parameter.dingding.url.secretRef != _|_ && parameter.dingding.url.value == _|_ {
read: op.#Read & {
value: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: parameter.dingding.url.fromSecret.name
name: parameter.dingding.url.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.dingding.url.fromSecret.key])
decoded: base64.Decode(null, read.value.data[parameter.dingding.url.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
ding2: op.#DingTalk & {
message: parameter.dingding.message
@@ -165,25 +173,25 @@ spec:
}
slack: op.#Steps & {
if parameter.slack != _|_ {
if parameter.slack.url.address != _|_ {
if parameter.slack.url.value != _|_ {
slack1: op.#Slack & {
message: parameter.slack.message
slackUrl: parameter.slack.url.address
slackUrl: parameter.slack.url.value
}
}
if parameter.slack.url.fromSecret != _|_ && parameter.slack.url.address == _|_ {
if parameter.slack.url.secretRef != _|_ && parameter.slack.url.value == _|_ {
read: op.#Read & {
value: {
kind: "Secret"
apiVersion: "v1"
metadata: {
name: parameter.slack.url.fromSecret.name
name: parameter.slack.url.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.slack.url.fromSecret.key])
decoded: base64.Decode(null, read.value.data[parameter.slack.url.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
slack2: op.#Slack & {
message: parameter.slack.message
@@ -192,4 +200,48 @@ spec:
}
}
}
email: op.#Steps & {
if parameter.email != _|_ {
if parameter.email.from.password.value != _|_ {
email1: op.#SendEmail & {
from: {
address: parameter.email.from.value
alias: parameter.email.from.alias
password: parameter.email.from.password.value
host: parameter.email.from.host
port: parameter.email.from.port
}
to: parameter.email.to
content: parameter.email.content
}
}
if parameter.email.from.password.secretRef != _|_ && parameter.email.from.password.value == _|_ {
read: op.#Read & {
value: {
kind: "Secret"
apiVersion: "v1"
metadata: {
name: parameter.email.from.password.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.email.from.password.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
email2: op.#SendEmail & {
from: {
address: parameter.email.from.value
alias: parameter.email.from.alias
password: stringValue.str
host: parameter.email.from.host
port: parameter.email.from.port
}
to: parameter.email.to
content: parameter.email.content
}
}
}
}

View File

@@ -24,7 +24,7 @@ spec:
dingding:
# directly specify the webhook url
url:
address: <dingding-url>
value: <dingding-url>
message:
msgtype: text
text:
@@ -32,10 +32,27 @@ spec:
slack:
url:
# use url in secret
formSecret:
secretRef:
name: <secret-name>
key: <secret-key>
message:
text: Hello KubeVela
email:
from:
address: <sender-email-address>
alias: <sender-alias>
password:
# secretRef:
# name: <secret-name>
# key: <secret-key>
value: <sender-password>
host: <email host like smtp.gmail.com>
port: <email port, optional, default to 587>
to:
- kubevela1@gmail.com
- kubevela2@gmail.com
content:
subject: test-subject
body: test-body
- name: first-server
type: apply-application

3
go.mod
View File

@@ -7,6 +7,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.1.1
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8
github.com/agiledragon/gomonkey/v2 v2.3.0
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/briandowns/spinner v1.11.1
@@ -54,6 +55,8 @@ require (
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
golang.org/x/tools v0.1.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gotest.tools v2.2.0+incompatible
helm.sh/helm/v3 v3.6.1

6
go.sum
View File

@@ -168,6 +168,8 @@ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agiledragon/gomonkey/v2 v2.3.0 h1:olsJjnSDpvqWl4FHByeMUs/r54/az+gQitU3VeEbC98=
github.com/agiledragon/gomonkey/v2 v2.3.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -2289,6 +2291,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2303,6 +2307,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw=
gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@@ -142,6 +142,8 @@ import (
#ConvertString: convert.#String
#SendEmail: email.#Send
#Load: oam.#LoadComponets
#Steps: {

19
pkg/stdlib/pkgs/email.cue Normal file
View File

@@ -0,0 +1,19 @@
#Send: {
#do: "send"
#provider: "email"
from: {
address: string
alias?: string
password: string
host: string
port: int
}
to: [...string]
content: {
subject: string
body: string
}
stepID: context.stepSessionID
...
}

View File

@@ -20,8 +20,6 @@ import (
"errors"
"testing"
"gotest.tools/assert"
"github.com/stretchr/testify/require"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
@@ -69,6 +67,7 @@ func TestInstall(t *testing.T) {
p := providers.NewProviders()
Install(p)
h, ok := p.GetHandler("convert", "string")
assert.Equal(t, ok, true)
assert.Equal(t, h != nil, true)
r := require.New(t)
r.Equal(ok, true)
r.Equal(h != nil, true)
}

View File

@@ -0,0 +1,136 @@
/*
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 email
import (
"fmt"
"sync"
"gopkg.in/gomail.v2"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
wfContext "github.com/oam-dev/kubevela/pkg/workflow/context"
"github.com/oam-dev/kubevela/pkg/workflow/providers"
"github.com/oam-dev/kubevela/pkg/workflow/types"
)
const (
// ProviderName is provider name for install.
ProviderName = "email"
)
type provider struct {
}
type sender struct {
Address string `json:"address"`
Alias string `json:"alias,omitempty"`
Password string `json:"password"`
Host string `json:"host"`
Port int `json:"port"`
}
type content struct {
Subject string `json:"subject"`
Body string `json:"body"`
}
var emailRoutine sync.Map
// Send sends email
func (h *provider) Send(ctx wfContext.Context, v *value.Value, act types.Action) error {
stepID, err := v.LookupValue("stepID")
if err != nil {
return err
}
id, err := stepID.String()
if err != nil {
return err
}
routine, ok := emailRoutine.Load(id)
if ok {
switch routine {
case "success":
emailRoutine.Delete(id)
return nil
case "initializing", "sending":
act.Wait("wait for the email")
return nil
default:
emailRoutine.Delete(id)
return fmt.Errorf("failed to send email: %v", routine)
}
} else {
emailRoutine.Store(id, "initializing")
}
s, err := v.LookupValue("from")
if err != nil {
return err
}
senderValue := &sender{}
if err := s.UnmarshalTo(senderValue); err != nil {
return err
}
r, err := v.LookupValue("to")
if err != nil {
return err
}
receiverValue := &[]string{}
if err := r.UnmarshalTo(receiverValue); err != nil {
return err
}
c, err := v.LookupValue("content")
if err != nil {
return err
}
contentValue := &content{}
if err := c.UnmarshalTo(contentValue); err != nil {
return err
}
m := gomail.NewMessage()
m.SetAddressHeader("From", senderValue.Address, senderValue.Alias)
m.SetHeader("To", *receiverValue...)
m.SetHeader("Subject", contentValue.Subject)
m.SetBody("text/html", contentValue.Body)
dial := gomail.NewDialer(senderValue.Host, senderValue.Port, senderValue.Address, senderValue.Password)
go func() {
if routine, ok := emailRoutine.Load(id); ok && routine == "initializing" {
emailRoutine.Store(id, "sending")
if err := dial.DialAndSend(m); err != nil {
emailRoutine.Store(id, err.Error())
return
}
emailRoutine.Store(id, "success")
}
}()
act.Wait("wait for the email")
return nil
}
// Install register handlers to provider discover.
func Install(p providers.Providers) {
prd := &provider{}
p.Register(ProviderName, map[string]providers.Handler{
"send": prd.Send,
})
}

View File

@@ -0,0 +1,162 @@
/*
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 email
import (
"errors"
"fmt"
"reflect"
"testing"
"time"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
"github.com/oam-dev/kubevela/pkg/workflow/providers"
"github.com/oam-dev/kubevela/pkg/workflow/providers/mock"
. "github.com/agiledragon/gomonkey/v2"
"github.com/stretchr/testify/require"
"gopkg.in/gomail.v2"
)
func TestSendEmail(t *testing.T) {
var dial *gomail.Dialer
testCases := map[string]struct {
from string
expectedErr error
errMsg string
}{
"success": {
from: `
from: {
address: "kubevela@gmail.com"
alias: "kubevela-bot"
password: "pwd"
host: "smtp.test.com"
port: 465
}
to: ["user1@gmail.com", "user2@gmail.com"]
content: {
subject: "Subject"
body: "Test body."
}
stepID: "success"
`,
},
"no-step-id": {
from: ``,
expectedErr: errors.New("var(path=stepID) not exist"),
},
"no-sender": {
from: `stepID:"no-sender"`,
expectedErr: errors.New("var(path=from) not exist"),
},
"no-receiver": {
from: `
from: {
address: "kubevela@gmail.com"
alias: "kubevela-bot"
password: "pwd"
host: "smtp.test.com"
port: 465
}
stepID: "no-receiver"
`,
expectedErr: errors.New("var(path=to) not exist"),
},
"no-content": {
from: `
from: {
address: "kubevela@gmail.com"
alias: "kubevela-bot"
password: "pwd"
host: "smtp.test.com"
port: 465
}
to: ["user1@gmail.com", "user2@gmail.com"]
stepID: "no-content"
`,
expectedErr: errors.New("var(path=content) not exist"),
},
"send-fail": {
from: `
from: {
address: "kubevela@gmail.com"
alias: "kubevela-bot"
password: "pwd"
host: "smtp.test.com"
port: 465
}
to: ["user1@gmail.com", "user2@gmail.com"]
content: {
subject: "Subject"
body: "Test body."
}
stepID: "send-fail"
`,
errMsg: "fail to send",
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
r := require.New(t)
patch := ApplyMethod(reflect.TypeOf(dial), "DialAndSend", func(_ *gomail.Dialer, _ ...*gomail.Message) error {
return nil
})
defer patch.Reset()
act := &mock.Action{}
if tc.errMsg != "" {
patch.Reset()
patch = ApplyMethod(reflect.TypeOf(dial), "DialAndSend", func(_ *gomail.Dialer, _ ...*gomail.Message) error {
return errors.New(tc.errMsg)
})
defer patch.Reset()
}
v, err := value.NewValue(tc.from, nil, "")
r.NoError(err)
prd := &provider{}
err = prd.Send(nil, v, act)
if tc.expectedErr != nil {
r.Equal(tc.expectedErr.Error(), err.Error())
return
}
r.NoError(err)
r.Equal(act.Phase, "Wait")
// mock reconcile
time.Sleep(time.Second)
err = prd.Send(nil, v, act)
if tc.errMsg != "" {
r.Equal(fmt.Errorf("failed to send email: %s", tc.errMsg), err)
return
}
r.NoError(err)
})
}
}
func TestInstall(t *testing.T) {
p := providers.NewProviders()
Install(p)
h, ok := p.GetHandler("email", "send")
r := require.New(t)
r.Equal(ok, true)
r.Equal(h != nil, true)
}

View File

@@ -0,0 +1,41 @@
/*
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 mock
// Action ...
type Action struct {
Phase string
Message string
}
// Suspend ...
func (act *Action) Suspend(message string) {
act.Phase = "Suspend"
act.Message = message
}
// Terminate ...
func (act *Action) Terminate(message string) {
act.Phase = "Terminate"
act.Message = message
}
// Wait ...
func (act *Action) Wait(message string) {
act.Phase = "Wait"
act.Message = message
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
"github.com/oam-dev/kubevela/pkg/workflow/providers/mock"
)
func TestParser(t *testing.T) {
@@ -35,7 +36,7 @@ func TestParser(t *testing.T) {
p := &provider{
apply: simpleComponentApplyForTest,
}
act := &mockAction{}
act := &mock.Action{}
v, err := value.NewValue("", nil, "")
assert.NilError(t, err)
err = p.ApplyComponent(nil, v, act)
@@ -73,12 +74,12 @@ metadata: {
}
`)
assert.Equal(t, act.phase, "Wait")
assert.Equal(t, act.Phase, "Wait")
testHealthy = true
act = &mockAction{}
act = &mock.Action{}
_, err = value.NewValue("", nil, "")
assert.NilError(t, err)
assert.Equal(t, act.phase, "")
assert.Equal(t, act.Phase, "")
}
func TestLoadComponent(t *testing.T) {
@@ -142,22 +143,3 @@ func simpleComponentApplyForTest(comp common.ApplicationComponent, _ *value.Valu
traits := []*unstructured.Unstructured{trait}
return workload, traits, testHealthy, nil
}
type mockAction struct {
phase string
message string
}
func (act *mockAction) Suspend(message string) {
act.phase = "Suspend"
act.message = message
}
func (act *mockAction) Terminate(message string) {
act.phase = "Terminate"
act.message = message
}
func (act *mockAction) Wait(message string) {
act.phase = "Wait"
act.message = message
}

View File

@@ -29,6 +29,7 @@ import (
wfContext "github.com/oam-dev/kubevela/pkg/workflow/context"
"github.com/oam-dev/kubevela/pkg/workflow/providers"
"github.com/oam-dev/kubevela/pkg/workflow/providers/convert"
"github.com/oam-dev/kubevela/pkg/workflow/providers/email"
"github.com/oam-dev/kubevela/pkg/workflow/providers/http"
"github.com/oam-dev/kubevela/pkg/workflow/providers/workspace"
"github.com/oam-dev/kubevela/pkg/workflow/tasks/custom"
@@ -74,6 +75,7 @@ func NewTaskDiscover(providerHandlers providers.Providers, pd *packages.PackageD
workspace.Install(providerHandlers)
http.Install(providerHandlers)
convert.Install(providerHandlers)
email.Install(providerHandlers)
templateLoader := template.NewTemplateLoader(cli, dm)
return &taskDiscover{
builtins: map[string]types.TaskGenerator{

View File

@@ -13,13 +13,7 @@ template: {
parameter: {
dingding?: {
url: {
address?: string
fromSecret?: {
name: string
key: string
}
}
url: value | secretRef
message: {
text?: *null | {
content: string
@@ -64,13 +58,7 @@ template: {
}
slack?: {
url: {
address?: string
fromSecret?: {
name: string
key: string
}
}
url: value | secretRef
message: {
text: string
blocks?: *null | [...block]
@@ -82,6 +70,21 @@ template: {
mrkdwn?: *true | bool
}
}
email?: {
from: {
address: string
alias?: string
password: value | secretRef
host: string
port: *587 | int
}
to: [...string]
content: {
subject: string
body: string
}
}
}
block: {
@@ -134,28 +137,35 @@ template: {
url?: string
}
secretRef: {
name: string
key: string
}
value: string
// send webhook notification
ding: op.#Steps & {
if parameter.dingding != _|_ {
if parameter.dingding.url.address != _|_ {
if parameter.dingding.url.value != _|_ {
ding1: op.#DingTalk & {
message: parameter.dingding.message
dingUrl: parameter.dingding.url.address
dingUrl: parameter.dingding.url.value
}
}
if parameter.dingding.url.fromSecret != _|_ && parameter.dingding.url.address == _|_ {
if parameter.dingding.url.secretRef != _|_ && parameter.dingding.url.value == _|_ {
read: op.#Read & {
value: {
apiVersion: "v1"
kind: "Secret"
metadata: {
name: parameter.dingding.url.fromSecret.name
name: parameter.dingding.url.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.dingding.url.fromSecret.key])
decoded: base64.Decode(null, read.value.data[parameter.dingding.url.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
ding2: op.#DingTalk & {
message: parameter.dingding.message
@@ -167,25 +177,25 @@ template: {
slack: op.#Steps & {
if parameter.slack != _|_ {
if parameter.slack.url.address != _|_ {
if parameter.slack.url.value != _|_ {
slack1: op.#Slack & {
message: parameter.slack.message
slackUrl: parameter.slack.url.address
slackUrl: parameter.slack.url.value
}
}
if parameter.slack.url.fromSecret != _|_ && parameter.slack.url.address == _|_ {
if parameter.slack.url.secretRef != _|_ && parameter.slack.url.value == _|_ {
read: op.#Read & {
value: {
kind: "Secret"
apiVersion: "v1"
metadata: {
name: parameter.slack.url.fromSecret.name
name: parameter.slack.url.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.slack.url.fromSecret.key])
decoded: base64.Decode(null, read.value.data[parameter.slack.url.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
slack2: op.#Slack & {
message: parameter.slack.message
@@ -194,4 +204,49 @@ template: {
}
}
}
email: op.#Steps & {
if parameter.email != _|_ {
if parameter.email.from.password.value != _|_ {
email1: op.#SendEmail & {
from: {
address: parameter.email.from.value
alias: parameter.email.from.alias
password: parameter.email.from.password.value
host: parameter.email.from.host
port: parameter.email.from.port
}
to: parameter.email.to
content: parameter.email.content
}
}
if parameter.email.from.password.secretRef != _|_ && parameter.email.from.password.value == _|_ {
read: op.#Read & {
value: {
kind: "Secret"
apiVersion: "v1"
metadata: {
name: parameter.email.from.password.secretRef.name
namespace: context.namespace
}
}
}
decoded: base64.Decode(null, read.value.data[parameter.email.from.password.secretRef.key])
stringValue: op.#ConvertString & {bt: decoded}
email2: op.#SendEmail & {
from: {
address: parameter.email.from.value
alias: parameter.email.from.alias
password: stringValue.str
host: parameter.email.from.host
port: parameter.email.from.port
}
to: parameter.email.to
content: parameter.email.content
}
}
}
}
}