Add dapr annotate command (#873)

* Add dapr inject command

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* Add additional opts

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* Update comment regarding injection

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* fix some linting issues (more to go)

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* use existing const in tests

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* fix some more linting

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* fix some more linting

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* udpate tests

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* udpate tests

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* add option to run against a URL

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* refactor cmd/inject.go slightly

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* update example text

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* autogenerate appid if not present

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* Update comment

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* Add support for target namespace

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* Update comment

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* Add warning about app id generation to usage

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* fix linting issues

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* rename inject to annotate

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* go mod tidy

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* fix lint

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* update --help URL

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

* add README.md seciton

Signed-off-by: Joni Collinge <jonathancollinge@live.com>

Co-authored-by: Yaron Schneider <schneider.yaron@live.com>
This commit is contained in:
Joni Collinge 2022-06-02 16:00:16 +01:00 committed by GitHub
parent 8e9c675bf6
commit 0630996fb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2658 additions and 4 deletions

View File

@ -630,6 +630,40 @@ The default is `false`.
For more details, please run the command and check the examples to apply to your shell.
### Annotate a Kubernetes manifest
To add or modify dapr annotations on an existing Kubernetes manifest, use the `dapr annotate` command:
```bash
dapr annotate [flags] mydeployment.yaml
```
This will add the `dapr.io/enabled` and the `dapr.io/app-id` annotations. The dapr app id will be genereated using the format `<namespace>-<kind>-<name>` where the values are taken from the existing Kubernetes object metadata.
To provide your own dapr app id, provide the flag `--app-id`.
All dapr annotations are available to set if a value is provided for the appropriate flag on the `dapr annotate` command.
You can also provide the Kubernetes manifest via stdin:
```bash
kubectl get deploy mydeploy -o yaml | dapr annotate - | kubectl apply -f -
```
Or you can provide the Kubernetes manifest via a URL:
```bash
dapr annotate --log-level debug https://raw.githubusercontent.com/dapr/quickstarts/master/tutorials/hello-kubernetes/deploy/node.yaml | kubectl apply -f -
```
If the input contains multiple manifests then the command will search for the first appropriate one to apply the annotations. If you'd rather it applied to a specific manifest then you can provide the `--resource` flag with the value set to the name of the object you'd like to apply the annotations to. If you have a conflict between namespaces you can also provide the namespace via the `--namespace` flag to isolate the manifest you wish to target.
If you want to annotate multiple manifests, you can chain together the `dapr annotate` commands with each applying the annotation to a specific manifest.
```bash
kubectl get deploy -o yaml | dapr annotate -r nodeapp --log-level debug - | dapr annotate --log-level debug -r pythonapp - | kubectl apply -f -
```
## Reference for the Dapr CLI
See the [Reference Guide](https://docs.dapr.io/reference/cli/) for more information about individual Dapr commands.

359
cmd/annotate.go Normal file
View File

@ -0,0 +1,359 @@
/*
Copyright 2021 The Dapr 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 cmd
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/dapr/cli/pkg/kubernetes"
"github.com/dapr/cli/pkg/print"
)
var (
annotateTargetResource string
annotateTargetNamespace string
annotateAppID string
annotateAppPort int
annotateConfig string
annotateAppProtocol string
annotateEnableProfile bool
annotateLogLevel string
annotateAPITokenSecret string
annotateAppTokenSecret string
annotateLogAsJSON bool
annotateAppMaxConcurrency int
annotateEnableMetrics bool
annotateMetricsPort int
annotateEnableDebug bool
annotateEnv string
annotateCPULimit string
annotateMemoryLimit string
annotateCPURequest string
annotateMemoryRequest string
annotateListenAddresses string
annotateLivenessProbeDelay int
annotateLivenessProbeTimeout int
annotateLivenessProbePeriod int
annotateLivenessProbeThreshold int
annotateReadinessProbeDelay int
annotateReadinessProbeTimeout int
annotateReadinessProbePeriod int
annotateReadinessProbeThreshold int
annotateDaprImage string
annotateAppSSL bool
annotateMaxRequestBodySize int
annotateHTTPStreamRequestBody bool
annotateGracefulShutdownSeconds int
)
var AnnotateCmd = &cobra.Command{
Use: "annotate [flags] CONFIG-FILE",
Short: "Add dapr annotations to a Kubernetes configuration. Supported platforms: Kubernetes",
Example: `
# Annotate the first deployment found in the input
kubectl get deploy -l app=node -o yaml | dapr annotate - | kubectl apply -f -
# Annotate multiple deployments by name in a chain
kubectl get deploy -o yaml | dapr annotate -r nodeapp - | dapr annotate -r pythonapp - | kubectl apply -f -
# Annotate deployment in a specific namespace from file or directory by name
dapr annotate -r nodeapp -n namespace mydeploy.yaml | kubectl apply -f -
# Annotate deployment from url by name
dapr annotate -r nodeapp --log-level debug https://raw.githubusercontent.com/dapr/quickstarts/master/tutorials/hello-kubernetes/deploy/node.yaml | kubectl apply -f -
--------------------------------------------------------------------------------
WARNING: If an app id is not provided, we will generate one using the format '<namespace>-<kind>-<name>'.
--------------------------------------------------------------------------------
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
print.FailureStatusEvent(os.Stderr, "please specify a kubernetes resource file")
os.Exit(1)
}
input, err := readInput(args[0])
if err != nil {
print.FailureStatusEvent(os.Stderr, err.Error())
os.Exit(1)
}
var config kubernetes.K8sAnnotatorConfig
if annotateTargetResource != "" {
config = kubernetes.K8sAnnotatorConfig{
TargetResource: &annotateTargetResource,
} // nolint:exhaustivestruct
if annotateTargetNamespace != "" {
config.TargetNamespace = &annotateTargetNamespace
}
} else {
if annotateTargetNamespace != "" {
// The resource is empty but namespace is set, this
// is invalid as we cannot search for a resource
// if the identifier isn't provided.
print.FailureStatusEvent(os.Stderr, "--resource is required when --namespace is provided.")
os.Exit(1)
}
}
annotator := kubernetes.NewK8sAnnotator(config)
opts := getOptionsFromFlags()
if err := annotator.Annotate(input, os.Stdout, opts); err != nil {
print.FailureStatusEvent(os.Stderr, err.Error())
os.Exit(1)
}
},
}
func readInput(arg string) ([]io.Reader, error) {
var inputs []io.Reader
var err error
if arg == "-" {
// input is from stdin.
inputs = append(inputs, os.Stdin)
} else if isURL(arg) {
inputs, err = readInputsFromURL(arg)
if err != nil {
return nil, err
}
} else {
// input is from file or dir.
inputs, err = readInputsFromFS(arg)
if err != nil {
return nil, err
}
}
return inputs, nil
}
func readInputsFromURL(url string) ([]io.Reader, error) {
resp, err := http.Get(url) // #nosec
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unable to read from %s: %d - %s", url, resp.StatusCode, resp.Status)
}
var b []byte
b, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
reader := bytes.NewReader(b)
return []io.Reader{reader}, nil
}
func isURL(maybeURL string) bool {
url, err := url.ParseRequestURI(maybeURL)
if err != nil {
return false
}
return url.Host != "" && url.Scheme != ""
}
func readInputsFromFS(path string) ([]io.Reader, error) {
stat, err := os.Stat(path)
if err != nil {
return nil, err
}
if !stat.IsDir() {
// input is a file.
var file *os.File
file, err = os.Open(path)
if err != nil {
return nil, err
}
return []io.Reader{file}, nil
}
// input is a directory.
var inputs []io.Reader
err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
inputs = append(inputs, file)
return nil
})
if err != nil {
return nil, err
}
return inputs, nil
}
func getOptionsFromFlags() kubernetes.AnnotateOptions {
// TODO: Use a pointer for int flag where zero is nil not -1.
o := []kubernetes.AnnoteOption{}
if annotateAppID != "" {
o = append(o, kubernetes.WithAppID(annotateAppID))
}
if annotateConfig != "" {
o = append(o, kubernetes.WithConfig(annotateConfig))
}
if annotateAppPort != -1 {
o = append(o, kubernetes.WithAppPort(annotateAppPort))
}
if annotateAppProtocol != "" {
o = append(o, kubernetes.WithAppProtocol(annotateAppProtocol))
}
if annotateEnableProfile {
o = append(o, kubernetes.WithProfileEnabled())
}
if annotateLogLevel != "" {
o = append(o, kubernetes.WithLogLevel(annotateLogLevel))
}
if annotateAPITokenSecret != "" {
o = append(o, kubernetes.WithAPITokenSecret(annotateAPITokenSecret))
}
if annotateAppTokenSecret != "" {
o = append(o, kubernetes.WithAppTokenSecret(annotateAppTokenSecret))
}
if annotateLogAsJSON {
o = append(o, kubernetes.WithLogAsJSON())
}
if annotateAppMaxConcurrency != -1 {
o = append(o, kubernetes.WithAppMaxConcurrency(annotateAppMaxConcurrency))
}
if annotateEnableMetrics {
o = append(o, kubernetes.WithMetricsEnabled())
}
if annotateMetricsPort != -1 {
o = append(o, kubernetes.WithMetricsPort(annotateMetricsPort))
}
if annotateEnableDebug {
o = append(o, kubernetes.WithDebugEnabled())
}
if annotateEnv != "" {
o = append(o, kubernetes.WithEnv(annotateEnv))
}
if annotateCPULimit != "" {
o = append(o, kubernetes.WithCPULimit(annotateCPULimit))
}
if annotateMemoryLimit != "" {
o = append(o, kubernetes.WithMemoryLimit(annotateMemoryLimit))
}
if annotateCPURequest != "" {
o = append(o, kubernetes.WithCPURequest(annotateCPURequest))
}
if annotateMemoryRequest != "" {
o = append(o, kubernetes.WithMemoryRequest(annotateMemoryRequest))
}
if annotateListenAddresses != "" {
o = append(o, kubernetes.WithListenAddresses(annotateListenAddresses))
}
if annotateLivenessProbeDelay != -1 {
o = append(o, kubernetes.WithLivenessProbeDelay(annotateLivenessProbeDelay))
}
if annotateLivenessProbeTimeout != -1 {
o = append(o, kubernetes.WithLivenessProbeTimeout(annotateLivenessProbeTimeout))
}
if annotateLivenessProbePeriod != -1 {
o = append(o, kubernetes.WithLivenessProbePeriod(annotateLivenessProbePeriod))
}
if annotateLivenessProbeThreshold != -1 {
o = append(o, kubernetes.WithLivenessProbeThreshold(annotateLivenessProbeThreshold))
}
if annotateReadinessProbeDelay != -1 {
o = append(o, kubernetes.WithReadinessProbeDelay(annotateReadinessProbeDelay))
}
if annotateReadinessProbeTimeout != -1 {
o = append(o, kubernetes.WithReadinessProbeTimeout(annotateReadinessProbeTimeout))
}
if annotateReadinessProbePeriod != -1 {
o = append(o, kubernetes.WithReadinessProbePeriod(annotateReadinessProbePeriod))
}
if annotateReadinessProbeThreshold != -1 {
o = append(o, kubernetes.WithReadinessProbeThreshold(annotateReadinessProbeThreshold))
}
if annotateDaprImage != "" {
o = append(o, kubernetes.WithDaprImage(annotateDaprImage))
}
if annotateAppSSL {
o = append(o, kubernetes.WithAppSSL())
}
if annotateMaxRequestBodySize != -1 {
o = append(o, kubernetes.WithMaxRequestBodySize(annotateMaxRequestBodySize))
}
if annotateHTTPStreamRequestBody {
o = append(o, kubernetes.WithHTTPStreamRequestBody())
}
if annotateGracefulShutdownSeconds != -1 {
o = append(o, kubernetes.WithGracefulShutdownSeconds(annotateGracefulShutdownSeconds))
}
return kubernetes.NewAnnotateOptions(o...)
}
func init() {
AnnotateCmd.Flags().StringVarP(&annotateTargetResource, "resource", "r", "", "The resource to target to annotate")
AnnotateCmd.Flags().StringVarP(&annotateTargetNamespace, "namespace", "n", "", "The namespace the resource target is in (can only be set if --resource is also set)")
AnnotateCmd.Flags().StringVarP(&annotateAppID, "app-id", "a", "", "The app id to annotate")
AnnotateCmd.Flags().IntVarP(&annotateAppPort, "app-port", "p", -1, "The port to expose the app on")
AnnotateCmd.Flags().StringVarP(&annotateConfig, "config", "c", "", "The config file to annotate")
AnnotateCmd.Flags().StringVar(&annotateAppProtocol, "app-protocol", "", "The protocol to use for the app")
AnnotateCmd.Flags().BoolVar(&annotateEnableProfile, "enable-profile", false, "Enable profiling")
AnnotateCmd.Flags().StringVar(&annotateLogLevel, "log-level", "", "The log level to use")
AnnotateCmd.Flags().StringVar(&annotateAPITokenSecret, "api-token-secret", "", "The secret to use for the API token")
AnnotateCmd.Flags().StringVar(&annotateAppTokenSecret, "app-token-secret", "", "The secret to use for the app token")
AnnotateCmd.Flags().BoolVar(&annotateLogAsJSON, "log-as-json", false, "Log as JSON")
AnnotateCmd.Flags().IntVar(&annotateAppMaxConcurrency, "app-max-concurrency", -1, "The maximum number of concurrent requests to allow")
AnnotateCmd.Flags().BoolVar(&annotateEnableMetrics, "enable-metrics", false, "Enable metrics")
AnnotateCmd.Flags().IntVar(&annotateMetricsPort, "metrics-port", -1, "The port to expose the metrics on")
AnnotateCmd.Flags().BoolVar(&annotateEnableDebug, "enable-debug", false, "Enable debug")
AnnotateCmd.Flags().StringVar(&annotateEnv, "env", "", "Environment variables to set (key value pairs, comma separated)")
AnnotateCmd.Flags().StringVar(&annotateCPULimit, "cpu-limit", "", "The CPU limit to set")
AnnotateCmd.Flags().StringVar(&annotateMemoryLimit, "memory-limit", "", "The memory limit to set")
AnnotateCmd.Flags().StringVar(&annotateCPURequest, "cpu-request", "", "The CPU request to set")
AnnotateCmd.Flags().StringVar(&annotateMemoryRequest, "memory-request", "", "The memory request to set")
AnnotateCmd.Flags().StringVar(&annotateListenAddresses, "listen-addresses", "", "The addresses to listen on")
AnnotateCmd.Flags().IntVar(&annotateLivenessProbeDelay, "liveness-probe-delay", -1, "The delay to use for the liveness probe")
AnnotateCmd.Flags().IntVar(&annotateLivenessProbeTimeout, "liveness-probe-timeout", -1, "The timeout to use for the liveness probe")
AnnotateCmd.Flags().IntVar(&annotateLivenessProbePeriod, "liveness-probe-period", -1, "The period to use for the liveness probe")
AnnotateCmd.Flags().IntVar(&annotateLivenessProbeThreshold, "liveness-probe-threshold", -1, "The threshold to use for the liveness probe")
AnnotateCmd.Flags().IntVar(&annotateReadinessProbeDelay, "readiness-probe-delay", -1, "The delay to use for the readiness probe")
AnnotateCmd.Flags().IntVar(&annotateReadinessProbeTimeout, "readiness-probe-timeout", -1, "The timeout to use for the readiness probe")
AnnotateCmd.Flags().IntVar(&annotateReadinessProbePeriod, "readiness-probe-period", -1, "The period to use for the readiness probe")
AnnotateCmd.Flags().IntVar(&annotateReadinessProbeThreshold, "readiness-probe-threshold", -1, "The threshold to use for the readiness probe")
AnnotateCmd.Flags().StringVar(&annotateDaprImage, "dapr-image", "", "The image to use for the dapr sidecar container")
AnnotateCmd.Flags().BoolVar(&annotateAppSSL, "app-ssl", false, "Enable SSL for the app")
AnnotateCmd.Flags().IntVar(&annotateMaxRequestBodySize, "max-request-body-size", -1, "The maximum request body size to use")
AnnotateCmd.Flags().BoolVar(&annotateHTTPStreamRequestBody, "http-stream-request-body", false, "Enable streaming request body for HTTP")
AnnotateCmd.Flags().IntVar(&annotateGracefulShutdownSeconds, "graceful-shutdown-seconds", -1, "The number of seconds to wait for the app to shutdown")
RootCmd.AddCommand(AnnotateCmd)
}

16
go.mod
View File

@ -3,6 +3,8 @@ module github.com/dapr/cli
go 1.17
require (
github.com/Azure/go-autorest/autorest v0.11.23 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect
github.com/Pallinder/sillyname-go v0.0.0-20130730142914-97aeae9e6ba1
github.com/briandowns/spinner v1.6.1
github.com/dapr/dapr v1.7.0
@ -30,14 +32,15 @@ require (
k8s.io/cli-runtime v0.23.4
k8s.io/client-go v0.23.4
k8s.io/helm v2.16.10+incompatible
sigs.k8s.io/yaml v1.3.0
)
require github.com/evanphx/json-patch v4.12.0+incompatible
require (
cloud.google.com/go v0.99.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.23 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
@ -52,6 +55,7 @@ require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/andybalholm/brotli v1.0.2 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
@ -67,7 +71,6 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
@ -80,6 +83,7 @@ require (
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.6 // indirect
@ -99,6 +103,7 @@ require (
github.com/jmoiron/sqlx v1.3.4 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/klauspost/compress v1.14.4 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
@ -147,10 +152,14 @@ require (
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.31.1-0.20211216042702-258a4c17b4f4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel v0.20.0 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 // indirect
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d // indirect
@ -178,7 +187,6 @@ require (
sigs.k8s.io/kustomize/api v0.10.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
replace (

13
go.sum
View File

@ -146,6 +146,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@ -706,12 +708,15 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/karrick/godirwalk v1.15.8 h1:7+rWAZPn9zuRxaIqqT8Ohs2Q2Ac0msBqwRdxNCr2VVs=
github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
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/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@ -1081,6 +1086,11 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.31.1-0.20211216042702-258a4c17b4f4 h1:UKbv1Y0TRLKcgacl2+v4xPt3iJLhjP0RCGwMOkUW1ko=
github.com/valyala/fasthttp v1.31.1-0.20211216042702-258a4c17b4f4/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
@ -1141,6 +1151,7 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
@ -1181,6 +1192,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -1275,6 +1287,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=

495
pkg/kubernetes/annotator.go Normal file
View File

@ -0,0 +1,495 @@
package kubernetes
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
"strings"
jsonpatch "github.com/evanphx/json-patch"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
yamlDecoder "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/yaml"
"github.com/dapr/dapr/pkg/injector"
)
const (
// Dapr annotation keys.
daprEnabledKey = "dapr.io/enabled"
daprAppPortKey = "dapr.io/app-port"
daprConfigKey = "dapr.io/config"
daprAppProtocolKey = "dapr.io/app-protocol"
daprAppIDKey = "dapr.io/app-id"
daprEnableProfilingKey = "dapr.io/enable-profiling"
daprLogLevelKey = "dapr.io/log-level"
daprAPITokenSecretKey = "dapr.io/api-token-secret" /* #nosec */
daprAppTokenSecretKey = "dapr.io/app-token-secret" /* #nosec */
daprLogAsJSONKey = "dapr.io/log-as-json"
daprAppMaxConcurrencyKey = "dapr.io/app-max-concurrency"
daprEnableMetricsKey = "dapr.io/enable-metrics"
daprMetricsPortKey = "dapr.io/metrics-port"
daprEnableDebugKey = "dapr.io/enable-debug"
daprDebugPortKey = "dapr.io/debug-port"
daprEnvKey = "dapr.io/env"
daprCPULimitKey = "dapr.io/sidecar-cpu-limit"
daprMemoryLimitKey = "dapr.io/sidecar-memory-limit"
daprCPURequestKey = "dapr.io/sidecar-cpu-request"
daprMemoryRequestKey = "dapr.io/sidecar-memory-request"
daprListenAddressesKey = "dapr.io/sidecar-listen-addresses"
daprLivenessProbeDelayKey = "dapr.io/sidecar-liveness-probe-delay-seconds"
daprLivenessProbeTimeoutKey = "dapr.io/sidecar-liveness-probe-timeout-seconds"
daprLivenessProbePeriodKey = "dapr.io/sidecar-liveness-probe-period-seconds"
daprLivenessProbeThresholdKey = "dapr.io/sidecar-liveness-probe-threshold"
daprReadinessProbeDelayKey = "dapr.io/sidecar-readiness-probe-delay-seconds"
daprReadinessProbeTimeoutKey = "dapr.io/sidecar-readiness-probe-timeout-seconds"
daprReadinessProbePeriodKey = "dapr.io/sidecar-readiness-probe-period-seconds"
daprReadinessProbeThresholdKey = "dapr.io/sidecar-readiness-probe-threshold"
daprImageKey = "dapr.io/sidecar-image"
daprAppSSLKey = "dapr.io/app-ssl"
daprMaxRequestBodySizeKey = "dapr.io/http-max-request-size"
daprReadBufferSizeKey = "dapr.io/http-read-buffer-size"
daprHTTPStreamRequestBodyKey = "dapr.io/http-stream-request-body"
daprGracefulShutdownSecondsKey = "dapr.io/graceful-shutdown-seconds"
// K8s kinds.
pod = "pod"
deployment = "deployment"
replicaset = "replicaset"
daemonset = "daemonset"
statefulset = "statefulset"
cronjob = "cronjob"
job = "job"
list = "list"
cronjobAnnotationsPath = "/spec/jobTemplate/spec/template/metadata/annotations"
podAnnotationsPath = "/metadata/annotations"
templateAnnotationsPath = "/spec/template/metadata/annotations"
)
type Annotator interface {
Annotate(io.Reader, io.Writer) error
}
type K8sAnnotator struct {
config K8sAnnotatorConfig
annotated bool
}
type K8sAnnotatorConfig struct {
// If TargetResource is set, we will search for it and then inject
// annotations on that target resource. If it is not set, we will
// update the first appropriate resource we find.
TargetResource *string
// If TargetNamespace is set, we will search for the target resource
// in the provided target namespace. If it is not set, we will
// just search for the first occurrence of the target resource.
TargetNamespace *string
}
func NewK8sAnnotator(config K8sAnnotatorConfig) *K8sAnnotator {
return &K8sAnnotator{
config: config,
annotated: false,
}
}
// Annotate injects dapr annotations into the kubernetes resource.
func (p *K8sAnnotator) Annotate(inputs []io.Reader, out io.Writer, opts AnnotateOptions) error {
for _, input := range inputs {
err := p.processInput(input, out, opts)
if err != nil {
return err
}
}
return nil
}
func (p *K8sAnnotator) processInput(input io.Reader, out io.Writer, opts AnnotateOptions) error {
reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(input, 4096))
var result []byte
iterations := 0
// Read from input and process until EOF or error.
for {
bytes, err := reader.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return err
}
// Check if the input is a list as
// these requires additional processing.
var metaType metav1.TypeMeta
if err = yaml.Unmarshal(bytes, &metaType); err != nil {
return err
}
kind := strings.ToLower(metaType.Kind)
if kind == list {
var sourceList corev1.List
if err = yaml.Unmarshal(bytes, &sourceList); err != nil {
return err
}
items := []runtime.RawExtension{}
for _, item := range sourceList.Items {
var processedYAML []byte
processedYAML, err = p.processYAML(item.Raw, opts)
if err != nil {
return err
}
var annotatedJSON []byte
annotatedJSON, err = yaml.YAMLToJSON(processedYAML)
if err != nil {
return err
}
items = append(items, runtime.RawExtension{Raw: annotatedJSON}) // nolint:exhaustivestruct
}
sourceList.Items = items
result, err = yaml.Marshal(sourceList)
if err != nil {
return err
}
} else {
var processedYAML []byte
processedYAML, err = p.processYAML(bytes, opts)
if err != nil {
return err
}
result = processedYAML
}
// Insert separator between documents.
if iterations > 0 {
out.Write([]byte("---\n"))
}
// Write result from processing into the writer.
_, err = out.Write(result)
if err != nil {
return err
}
iterations++
}
return nil
}
func (p *K8sAnnotator) processYAML(yamlBytes []byte, opts AnnotateOptions) ([]byte, error) {
var err error
var processedYAML []byte
if p.annotated {
// We can only inject dapr into a single resource per execution as the configuration
// options are scoped to a single resource e.g. app-id, port, etc. are specific to a
// dapr enabled resource. Instead we expect multiple runs to be piped together.
processedYAML = yamlBytes
} else {
var annotated bool
processedYAML, annotated, err = p.annotateYAML(yamlBytes, opts)
if err != nil {
return nil, err
}
if annotated {
// Record that we have injected into a document.
p.annotated = annotated
}
}
return processedYAML, nil
}
func (p *K8sAnnotator) annotateYAML(input []byte, config AnnotateOptions) ([]byte, bool, error) {
// We read the metadata again here so this method is encapsulated.
var metaType metav1.TypeMeta
if err := yaml.Unmarshal(input, &metaType); err != nil {
return nil, false, err
}
// If the input resource is a 'kind' that
// we want to inject dapr into, then we
// Unmarshal the input into the appropriate
// type and set the required fields to build
// a patch (path, value, op).
var path string
var annotations map[string]string
var name string
var ns string
kind := strings.ToLower(metaType.Kind)
switch kind {
case pod:
pod := &corev1.Pod{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, pod); err != nil {
return nil, false, err
}
name = pod.Name
annotations = pod.Annotations
path = podAnnotationsPath
ns = getNamespaceOrDefault(pod)
case cronjob:
cronjob := &batchv1beta1.CronJob{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, cronjob); err != nil {
return nil, false, err
}
name = cronjob.Name
annotations = cronjob.Spec.JobTemplate.Spec.Template.Annotations
path = cronjobAnnotationsPath
ns = getNamespaceOrDefault(cronjob)
case deployment:
deployment := &appsv1.Deployment{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, deployment); err != nil {
return nil, false, err
}
name = deployment.Name
annotations = deployment.Spec.Template.Annotations
path = templateAnnotationsPath
ns = getNamespaceOrDefault(deployment)
case replicaset:
replicaset := &appsv1.ReplicaSet{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, replicaset); err != nil {
return nil, false, err
}
name = replicaset.Name
annotations = replicaset.Spec.Template.Annotations
path = templateAnnotationsPath
ns = getNamespaceOrDefault(replicaset)
case job:
job := &batchv1.Job{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, job); err != nil {
return nil, false, err
}
name = job.Name
annotations = job.Spec.Template.Annotations
path = templateAnnotationsPath
ns = getNamespaceOrDefault(job)
case statefulset:
statefulset := &appsv1.StatefulSet{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, statefulset); err != nil {
return nil, false, err
}
name = statefulset.Name
annotations = statefulset.Spec.Template.Annotations
path = templateAnnotationsPath
ns = getNamespaceOrDefault(statefulset)
case daemonset:
daemonset := &appsv1.DaemonSet{} // nolint:exhaustivestruct
if err := yaml.Unmarshal(input, daemonset); err != nil {
return nil, false, err
}
name = daemonset.Name
annotations = daemonset.Spec.Template.Annotations
path = templateAnnotationsPath
ns = getNamespaceOrDefault(daemonset)
default:
// No annotation needed for this kind.
return input, false, nil
}
// TODO: Currently this is where we decide not to
// annotate dapr on this resource as it isn't the
// target we are looking for. This is a bit late
// so it would be good to find a earlier place to
// do this.
if p.config.TargetResource != nil && *p.config.TargetResource != "" {
if !strings.EqualFold(*p.config.TargetResource, name) {
// Not the resource name we're annotating.
return input, false, nil
}
if p.config.TargetNamespace != nil && *p.config.TargetNamespace != "" {
if !strings.EqualFold(*p.config.TargetNamespace, ns) {
// Not the namespace we're annotating.
return input, false, nil
}
}
}
// Get the dapr annotations and set them on the
// resources existing annotation map. This will
// override any existing conflicting annotations.
if annotations == nil {
annotations = make(map[string]string)
}
daprAnnotations := getDaprAnnotations(&config)
for k, v := range daprAnnotations {
// TODO: Should we log when we are overwriting?
// if _, exists := annotations[k]; exists {}.
annotations[k] = v
}
// Check if the app id has been set, if not, we'll
// use the resource metadata namespace, kind and name.
// For example: namespace-kind-name.
if _, appIDSet := annotations[daprAppIDKey]; !appIDSet {
annotations[daprAppIDKey] = fmt.Sprintf("%s-%s-%s", ns, kind, name)
}
// Create a patch operation for the annotations.
patchOps := []injector.PatchOperation{}
patchOps = append(patchOps, injector.PatchOperation{
Op: "add",
Path: path,
Value: annotations,
})
patchBytes, err := json.Marshal(patchOps)
if err != nil {
return nil, false, err
}
if len(patchBytes) == 0 {
return input, false, nil
}
patch, err := jsonpatch.DecodePatch(patchBytes)
if err != nil {
return nil, false, err
}
// As we are applying the patch as a json patch,
// we have to convert the current YAML resource to
// JSON, apply the patch and then convert back.
inputAsJSON, err := yaml.YAMLToJSON(input)
if err != nil {
return nil, false, err
}
annotatedAsJSON, err := patch.Apply(inputAsJSON)
if err != nil {
return nil, false, err
}
annotatedAsYAML, err := yaml.JSONToYAML(annotatedAsJSON)
if err != nil {
return nil, false, err
}
return annotatedAsYAML, true, nil
}
type NamespacedObject interface {
GetNamespace() string
}
func getNamespaceOrDefault(obj NamespacedObject) string {
ns := obj.GetNamespace()
if ns == "" {
return "default"
}
return ns
}
func getDaprAnnotations(config *AnnotateOptions) map[string]string {
annotations := make(map[string]string)
annotations[daprEnabledKey] = "true"
if config.appID != nil {
annotations[daprAppIDKey] = *config.appID
}
if config.metricsEnabled != nil {
annotations[daprEnableMetricsKey] = strconv.FormatBool(*config.metricsEnabled)
}
if config.metricsPort != nil {
annotations[daprMetricsPortKey] = strconv.FormatInt(int64(*config.metricsPort), 10)
}
if config.appPort != nil {
annotations[daprAppPortKey] = strconv.FormatInt(int64(*config.appPort), 10)
}
if config.config != nil {
annotations[daprConfigKey] = *config.config
}
if config.appProtocol != nil {
annotations[daprAppProtocolKey] = *config.appProtocol
}
if config.profileEnabled != nil {
annotations[daprEnableProfilingKey] = strconv.FormatBool(*config.profileEnabled)
}
if config.logLevel != nil {
annotations[daprLogLevelKey] = *config.logLevel
}
if config.logAsJSON != nil {
annotations[daprLogAsJSONKey] = strconv.FormatBool(*config.logAsJSON)
}
if config.apiTokenSecret != nil {
annotations[daprAPITokenSecretKey] = *config.apiTokenSecret
}
if config.appTokenSecret != nil {
annotations[daprAppTokenSecretKey] = *config.appTokenSecret
}
if config.appMaxConcurrency != nil {
annotations[daprAppMaxConcurrencyKey] = strconv.FormatInt(int64(*config.appMaxConcurrency), 10)
}
if config.debugEnabled != nil {
annotations[daprEnableDebugKey] = strconv.FormatBool(*config.debugEnabled)
}
if config.debugPort != nil {
annotations[daprDebugPortKey] = strconv.FormatInt(int64(*config.debugPort), 10)
}
if config.env != nil {
annotations[daprEnvKey] = *config.env
}
if config.cpuLimit != nil {
annotations[daprCPULimitKey] = *config.cpuLimit
}
if config.memoryLimit != nil {
annotations[daprMemoryLimitKey] = *config.memoryLimit
}
if config.cpuRequest != nil {
annotations[daprCPURequestKey] = *config.cpuRequest
}
if config.memoryRequest != nil {
annotations[daprMemoryRequestKey] = *config.memoryRequest
}
if config.listenAddresses != nil {
annotations[daprListenAddressesKey] = *config.listenAddresses
}
if config.livenessProbeDelay != nil {
annotations[daprLivenessProbeDelayKey] = strconv.FormatInt(int64(*config.livenessProbeDelay), 10)
}
if config.livenessProbeTimeout != nil {
annotations[daprLivenessProbeTimeoutKey] = strconv.FormatInt(int64(*config.livenessProbeTimeout), 10)
}
if config.livenessProbePeriod != nil {
annotations[daprLivenessProbePeriodKey] = strconv.FormatInt(int64(*config.livenessProbePeriod), 10)
}
if config.livenessProbeThreshold != nil {
annotations[daprLivenessProbeThresholdKey] = strconv.FormatInt(int64(*config.livenessProbeThreshold), 10)
}
if config.readinessProbeDelay != nil {
annotations[daprReadinessProbeDelayKey] = strconv.FormatInt(int64(*config.readinessProbeDelay), 10)
}
if config.readinessProbeTimeout != nil {
annotations[daprReadinessProbeTimeoutKey] = strconv.FormatInt(int64(*config.readinessProbeTimeout), 10)
}
if config.readinessProbePeriod != nil {
annotations[daprReadinessProbePeriodKey] = strconv.FormatInt(int64(*config.readinessProbePeriod), 10)
}
if config.readinessProbeThreshold != nil {
annotations[daprReadinessProbeThresholdKey] = strconv.FormatInt(int64(*config.readinessProbeThreshold), 10)
}
if config.image != nil {
annotations[daprImageKey] = *config.image
}
if config.appSSL != nil {
annotations[daprAppSSLKey] = strconv.FormatBool(*config.appSSL)
}
if config.maxRequestBodySize != nil {
annotations[daprMaxRequestBodySizeKey] = strconv.FormatInt(int64(*config.maxRequestBodySize), 10)
}
if config.readBufferSize != nil {
annotations[daprReadBufferSizeKey] = strconv.FormatInt(int64(*config.readBufferSize), 10)
}
if config.httpStreamRequestBody != nil {
annotations[daprHTTPStreamRequestBodyKey] = strconv.FormatBool(*config.httpStreamRequestBody)
}
if config.gracefulShutdownSeconds != nil {
annotations[daprGracefulShutdownSecondsKey] = strconv.FormatInt(int64(*config.gracefulShutdownSeconds), 10)
}
return annotations
}

View File

@ -0,0 +1,259 @@
package kubernetes
// AnnotateOptions configure the injection behavior.
type AnnotateOptions struct {
appID *string
metricsEnabled *bool
metricsPort *int
appPort *int
config *string
appProtocol *string
profileEnabled *bool
logLevel *string
apiTokenSecret *string
appTokenSecret *string
logAsJSON *bool
appMaxConcurrency *int
debugEnabled *bool
debugPort *int
env *string
cpuLimit *string
memoryLimit *string
cpuRequest *string
memoryRequest *string
listenAddresses *string
livenessProbeDelay *int
livenessProbeTimeout *int
livenessProbePeriod *int
livenessProbeThreshold *int
readinessProbeDelay *int
readinessProbeTimeout *int
readinessProbePeriod *int
readinessProbeThreshold *int
image *string
appSSL *bool
maxRequestBodySize *int
readBufferSize *int
httpStreamRequestBody *bool
gracefulShutdownSeconds *int
}
type AnnoteOption func(*AnnotateOptions)
func NewAnnotateOptions(opts ...AnnoteOption) AnnotateOptions {
config := AnnotateOptions{} // nolint:exhaustivestruct
for _, opt := range opts {
opt(&config)
}
return config
}
func WithAppID(appID string) AnnoteOption {
return func(config *AnnotateOptions) {
config.appID = &appID
}
}
func WithMetricsEnabled() AnnoteOption {
return func(config *AnnotateOptions) {
enabled := true
config.metricsEnabled = &enabled
}
}
func WithMetricsPort(port int) AnnoteOption {
return func(config *AnnotateOptions) {
config.metricsPort = &port
}
}
func WithAppPort(port int) AnnoteOption {
return func(config *AnnotateOptions) {
config.appPort = &port
}
}
func WithConfig(cfg string) AnnoteOption {
return func(config *AnnotateOptions) {
config.config = &cfg
}
}
func WithAppProtocol(protocol string) AnnoteOption {
return func(config *AnnotateOptions) {
config.appProtocol = &protocol
}
}
func WithProfileEnabled() AnnoteOption {
return func(config *AnnotateOptions) {
enabled := true
config.profileEnabled = &enabled
}
}
func WithLogLevel(logLevel string) AnnoteOption {
return func(config *AnnotateOptions) {
config.logLevel = &logLevel
}
}
func WithAPITokenSecret(apiTokenSecret string) AnnoteOption {
return func(config *AnnotateOptions) {
config.apiTokenSecret = &apiTokenSecret
}
}
func WithAppTokenSecret(appTokenSecret string) AnnoteOption {
return func(config *AnnotateOptions) {
config.appTokenSecret = &appTokenSecret
}
}
func WithLogAsJSON() AnnoteOption {
return func(config *AnnotateOptions) {
enabled := true
config.logAsJSON = &enabled
}
}
func WithAppMaxConcurrency(maxConcurrency int) AnnoteOption {
return func(config *AnnotateOptions) {
config.appMaxConcurrency = &maxConcurrency
}
}
func WithDebugEnabled() AnnoteOption {
return func(config *AnnotateOptions) {
enabled := true
config.debugEnabled = &enabled
}
}
func WithDebugPort(debugPort int) AnnoteOption {
return func(config *AnnotateOptions) {
config.debugPort = &debugPort
}
}
func WithEnv(env string) AnnoteOption {
return func(config *AnnotateOptions) {
config.env = &env
}
}
func WithCPULimit(cpuLimit string) AnnoteOption {
return func(config *AnnotateOptions) {
config.cpuLimit = &cpuLimit
}
}
func WithMemoryLimit(memoryLimit string) AnnoteOption {
return func(config *AnnotateOptions) {
config.memoryLimit = &memoryLimit
}
}
func WithCPURequest(cpuRequest string) AnnoteOption {
return func(config *AnnotateOptions) {
config.cpuRequest = &cpuRequest
}
}
func WithMemoryRequest(memoryRequest string) AnnoteOption {
return func(config *AnnotateOptions) {
config.memoryRequest = &memoryRequest
}
}
func WithListenAddresses(listenAddresses string) AnnoteOption {
return func(config *AnnotateOptions) {
config.listenAddresses = &listenAddresses
}
}
func WithLivenessProbeDelay(livenessProbeDelay int) AnnoteOption {
return func(config *AnnotateOptions) {
config.livenessProbeDelay = &livenessProbeDelay
}
}
func WithLivenessProbeTimeout(livenessProbeTimeout int) AnnoteOption {
return func(config *AnnotateOptions) {
config.livenessProbeTimeout = &livenessProbeTimeout
}
}
func WithLivenessProbePeriod(livenessProbePeriod int) AnnoteOption {
return func(config *AnnotateOptions) {
config.livenessProbePeriod = &livenessProbePeriod
}
}
func WithLivenessProbeThreshold(livenessProbeThreshold int) AnnoteOption {
return func(config *AnnotateOptions) {
config.livenessProbeThreshold = &livenessProbeThreshold
}
}
func WithReadinessProbeDelay(readinessProbeDelay int) AnnoteOption {
return func(config *AnnotateOptions) {
config.readinessProbeDelay = &readinessProbeDelay
}
}
func WithReadinessProbeTimeout(readinessProbeTimeout int) AnnoteOption {
return func(config *AnnotateOptions) {
config.readinessProbeTimeout = &readinessProbeTimeout
}
}
func WithReadinessProbePeriod(readinessProbePeriod int) AnnoteOption {
return func(config *AnnotateOptions) {
config.readinessProbePeriod = &readinessProbePeriod
}
}
func WithReadinessProbeThreshold(readinessProbeThreshold int) AnnoteOption {
return func(config *AnnotateOptions) {
config.readinessProbeThreshold = &readinessProbeThreshold
}
}
func WithDaprImage(image string) AnnoteOption {
return func(config *AnnotateOptions) {
config.image = &image
}
}
func WithAppSSL() AnnoteOption {
return func(config *AnnotateOptions) {
enabled := true
config.appSSL = &enabled
}
}
func WithMaxRequestBodySize(maxRequestBodySize int) AnnoteOption {
return func(config *AnnotateOptions) {
config.maxRequestBodySize = &maxRequestBodySize
}
}
func WithReadBufferSize(readBufferSize int) AnnoteOption {
return func(config *AnnotateOptions) {
config.readBufferSize = &readBufferSize
}
}
func WithHTTPStreamRequestBody() AnnoteOption {
return func(config *AnnotateOptions) {
enabled := true
config.httpStreamRequestBody = &enabled
}
}
func WithGracefulShutdownSeconds(gracefulShutdownSeconds int) AnnoteOption {
return func(config *AnnotateOptions) {
config.gracefulShutdownSeconds = &gracefulShutdownSeconds
}
}

View File

@ -0,0 +1,451 @@
package kubernetes
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"sort"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
const (
annotatorTestDataDir = "testdata/annotator"
)
var (
podInDir = path.Join(annotatorTestDataDir, "pod/in")
podOutDir = path.Join(annotatorTestDataDir, "pod/out")
deploymentInDir = path.Join(annotatorTestDataDir, "deployment/in")
deploymentOutDir = path.Join(annotatorTestDataDir, "deployment/out")
multiInDir = path.Join(annotatorTestDataDir, "multi/in")
multiOutDir = path.Join(annotatorTestDataDir, "multi/out")
listInDir = path.Join(annotatorTestDataDir, "list/in")
listOutDir = path.Join(annotatorTestDataDir, "list/out")
)
type annotation struct {
targetResource string
targetNamespace string
optionFactory func() AnnotateOptions
}
// nolint
func TestAnnotate(t *testing.T) {
// Helper function used to order test documents.
sortDocs := func(docs []string) {
sort.Slice(docs, func(i, j int) bool {
if len(docs[i]) == len(docs[j]) {
panic("Cannot sort docs with the same length, please ensure tests docs are a unique length.")
}
return len(docs[i]) < len(docs[j])
})
}
configs := []struct {
testID string
annotations []annotation
inputFilePath string
expectedFilePath string
printOutput bool
}{
{
testID: "single targeted annotation of a pod (config 1)",
annotations: []annotation{
{
targetResource: "mypod",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("test-app"),
)
},
},
},
inputFilePath: path.Join(podInDir, "raw.yml"),
expectedFilePath: path.Join(podOutDir, "config_1.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single targeted annotation of a pod (config 2)",
annotations: []annotation{
{
targetResource: "mypod",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("test-app"),
WithProfileEnabled(),
WithLogLevel("info"),
WithDaprImage("custom-image"),
)
},
},
},
inputFilePath: path.Join(podInDir, "raw.yml"),
expectedFilePath: path.Join(podOutDir, "config_2.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single targeted annotation of a pod without an app id in default namespace (config 3)",
annotations: []annotation{
{
targetResource: "mypod",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions()
},
},
},
inputFilePath: path.Join(podInDir, "raw.yml"),
expectedFilePath: path.Join(podOutDir, "config_3.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single targeted annotation of a pod without an app id in a namespace (config 4)",
annotations: []annotation{
{
targetResource: "mypod",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions()
},
},
},
inputFilePath: path.Join(podInDir, "namespace.yml"),
expectedFilePath: path.Join(podOutDir, "config_4.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single targeted annotation of a deployment (config 1)",
annotations: []annotation{
{
targetResource: "nodeapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("nodeapp"),
WithAppPort(3000),
)
},
},
},
inputFilePath: path.Join(deploymentInDir, "raw.yml"),
expectedFilePath: path.Join(deploymentOutDir, "config_1.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "partial annotation of a deployment (config 2)",
annotations: []annotation{
{
targetResource: "nodeapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("nodeapp"),
WithAppPort(3000),
)
},
},
},
inputFilePath: path.Join(deploymentInDir, "partial.yml"),
expectedFilePath: path.Join(deploymentOutDir, "config_1.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single targeted annotation of multiple resources (config 1)",
annotations: []annotation{
{
targetResource: "divideapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("divideapp"),
WithAppPort(4000),
WithConfig("appconfig"),
)
},
},
},
inputFilePath: path.Join(multiInDir, "raw.yml"),
expectedFilePath: path.Join(multiOutDir, "config_1.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "multiple targeted annotations of multiple resources (config 2)",
annotations: []annotation{
{
targetResource: "subtractapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("subtractapp"),
WithAppPort(80),
WithConfig("appconfig"),
)
},
},
{
targetResource: "addapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("addapp"),
WithAppPort(6000),
WithConfig("appconfig"),
)
},
},
{
targetResource: "multiplyapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("multiplyapp"),
WithAppPort(5000),
WithConfig("appconfig"),
)
},
},
{
targetResource: "divideapp",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("divideapp"),
WithAppPort(4000),
WithConfig("appconfig"),
)
},
},
{
targetResource: "calculator-front-end",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("calculator-front-end"),
WithAppPort(8080),
WithConfig("appconfig"),
)
},
},
},
inputFilePath: path.Join(multiInDir, "raw.yml"),
expectedFilePath: path.Join(multiOutDir, "config_2.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single untargeted annotations of multiple resources (config 3)",
annotations: []annotation{
{
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("subtractapp"),
WithAppPort(80),
WithConfig("appconfig"),
)
},
},
},
inputFilePath: path.Join(multiInDir, "raw.yml"),
expectedFilePath: path.Join(multiOutDir, "config_3.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single targeted annotations of multiple resources with a namespace (config 4)",
annotations: []annotation{
{
targetResource: "subtractapp",
targetNamespace: "test1",
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("subtractapp"),
)
},
},
},
inputFilePath: path.Join(multiInDir, "namespace.yml"),
expectedFilePath: path.Join(multiOutDir, "config_4.yml"),
// printOutput: true, // Uncomment to debug.
},
{
testID: "single untargeted annotations of a list config",
annotations: []annotation{
{
optionFactory: func() AnnotateOptions {
return NewAnnotateOptions(
WithAppID("nodeapp"),
WithAppPort(3000),
)
},
},
},
inputFilePath: path.Join(listInDir, "raw.yml"),
expectedFilePath: path.Join(listOutDir, "config_1.yml"),
// printOutput: true, // Uncomment to debug.
},
}
for _, tt := range configs {
t.Run(tt.testID, func(t *testing.T) {
inputFile, err := os.Open(tt.inputFilePath)
assert.NoError(t, err)
defer func() {
err = inputFile.Close()
assert.NoError(t, err)
}()
// Iterate through all the annotations and pipe them together.
var out bytes.Buffer
in := []io.Reader{inputFile}
for i, annotation := range tt.annotations {
annotator := NewK8sAnnotator(K8sAnnotatorConfig{
TargetResource: &annotation.targetResource,
TargetNamespace: &annotation.targetNamespace,
})
annotateOptions := annotation.optionFactory()
out.Reset()
err = annotator.Annotate(in, &out, annotateOptions)
assert.NoError(t, err)
// if it isn't the last annotation then set input to this annotation output
// to support testing chained resources.
if i != len(tt.annotations)-1 {
outReader := strings.NewReader(out.String())
in = []io.Reader{outReader}
}
}
// Split the multi-document string into individual documents for comparison.
outString := out.String()
outDocs := strings.Split(outString, "---")
expected, err := ioutil.ReadFile(tt.expectedFilePath)
assert.NoError(t, err)
expectedString := string(expected)
expectedDocs := strings.Split(expectedString, "---")
// We must sort the documents to ensure we are comparing the correct documents.
// The content of the documents should be equivalent but it will not be the same
// as the order of keys are not being preserved. Therefore, we sort on the content
// length instead. This isn't perfect as additional character may be included but
// as long as we have enough spread between the documents we should be ok to use this
// to get an order. sortDocs will panic if it tries to compare content that is the
// same length as we would lose ordering but invalid orders are still possible.
sortDocs(outDocs)
sortDocs(expectedDocs)
assert.Equal(t, len(expectedDocs), len(outDocs))
for i := range expectedDocs {
if tt.printOutput {
t.Logf(outDocs[i])
}
assert.YAMLEq(t, expectedDocs[i], outDocs[i])
}
})
}
}
func TestGetDaprAnnotations(t *testing.T) {
t.Run("get dapr annotations", func(t *testing.T) {
appID := "test-app"
metricsPort := 9090
apiTokenSecret := "test-api-token-secret" // #nosec
appTokenSecret := "test-app-token-secret" // #nosec
appMaxConcurrency := 2
appPort := 8080
appProtocol := "http"
cpuLimit := "0.5"
memoryLimit := "512Mi"
cpuRequest := "0.1"
memoryRequest := "256Mi"
config := "appconfig"
debugPort := 9091
env := "key=value key1=value1"
listenAddresses := "0.0.0.0"
daprImage := "test-iamge"
maxRequestBodySize := 8
readBufferSize := 4
livenessProbeDelay := 10
livenessProbePeriod := 20
livenessProbeThreshold := 3
livenessProbeTimeout := 30
readinessProbeDelay := 40
readinessProbePeriod := 50
readinessProbeThreshold := 6
readinessProbeTimeout := 60
logLevel := "debug"
gracefulShutdownSeconds := 10
opts := NewAnnotateOptions(
WithAppID(appID),
WithMetricsEnabled(),
WithMetricsPort(metricsPort),
WithAPITokenSecret(apiTokenSecret),
WithAppTokenSecret(appTokenSecret),
WithAppMaxConcurrency(appMaxConcurrency),
WithAppPort(appPort),
WithAppProtocol(appProtocol),
WithAppSSL(),
WithCPULimit(cpuLimit),
WithMemoryLimit(memoryLimit),
WithCPURequest(cpuRequest),
WithMemoryRequest(memoryRequest),
WithConfig(config),
WithDebugEnabled(),
WithDebugPort(debugPort),
WithEnv(env),
WithLogAsJSON(),
WithListenAddresses(listenAddresses),
WithDaprImage(daprImage),
WithProfileEnabled(),
WithMaxRequestBodySize(maxRequestBodySize),
WithReadBufferSize(readBufferSize),
WithReadinessProbeDelay(readinessProbeDelay),
WithReadinessProbePeriod(readinessProbePeriod),
WithReadinessProbeThreshold(readinessProbeThreshold),
WithReadinessProbeTimeout(readinessProbeTimeout),
WithLivenessProbeDelay(livenessProbeDelay),
WithLivenessProbePeriod(livenessProbePeriod),
WithLivenessProbeThreshold(livenessProbeThreshold),
WithLivenessProbeTimeout(livenessProbeTimeout),
WithLogLevel(logLevel),
WithHTTPStreamRequestBody(),
WithGracefulShutdownSeconds(gracefulShutdownSeconds),
)
annotations := getDaprAnnotations(&opts)
assert.Equal(t, "true", annotations[daprEnabledKey])
assert.Equal(t, appID, annotations[daprAppIDKey])
assert.Equal(t, fmt.Sprintf("%d", appPort), annotations[daprAppPortKey])
assert.Equal(t, config, annotations[daprConfigKey])
assert.Equal(t, appProtocol, annotations[daprAppProtocolKey])
assert.Equal(t, "true", annotations[daprEnableProfilingKey])
assert.Equal(t, logLevel, annotations[daprLogLevelKey])
assert.Equal(t, apiTokenSecret, annotations[daprAPITokenSecretKey])
assert.Equal(t, appTokenSecret, annotations[daprAppTokenSecretKey])
assert.Equal(t, "true", annotations[daprLogAsJSONKey])
assert.Equal(t, fmt.Sprintf("%d", appMaxConcurrency), annotations[daprAppMaxConcurrencyKey])
assert.Equal(t, "true", annotations[daprEnableMetricsKey])
assert.Equal(t, fmt.Sprintf("%d", metricsPort), annotations[daprMetricsPortKey])
assert.Equal(t, "true", annotations[daprEnableDebugKey])
assert.Equal(t, fmt.Sprintf("%d", debugPort), annotations[daprDebugPortKey])
assert.Equal(t, env, annotations[daprEnvKey])
assert.Equal(t, cpuLimit, annotations[daprCPULimitKey])
assert.Equal(t, memoryLimit, annotations[daprMemoryLimitKey])
assert.Equal(t, cpuRequest, annotations[daprCPURequestKey])
assert.Equal(t, memoryRequest, annotations[daprMemoryRequestKey])
assert.Equal(t, listenAddresses, annotations[daprListenAddressesKey])
assert.Equal(t, fmt.Sprintf("%d", livenessProbeDelay), annotations[daprLivenessProbeDelayKey])
assert.Equal(t, fmt.Sprintf("%d", livenessProbeTimeout), annotations[daprLivenessProbeTimeoutKey])
assert.Equal(t, fmt.Sprintf("%d", livenessProbePeriod), annotations[daprLivenessProbePeriodKey])
assert.Equal(t, fmt.Sprintf("%d", livenessProbeThreshold), annotations[daprLivenessProbeThresholdKey])
assert.Equal(t, fmt.Sprintf("%d", readinessProbeDelay), annotations[daprReadinessProbeDelayKey])
assert.Equal(t, fmt.Sprintf("%d", readinessProbeTimeout), annotations[daprReadinessProbeTimeoutKey])
assert.Equal(t, fmt.Sprintf("%d", readinessProbePeriod), annotations[daprReadinessProbePeriodKey])
assert.Equal(t, fmt.Sprintf("%d", readinessProbeThreshold), annotations[daprReadinessProbeThresholdKey])
assert.Equal(t, daprImage, annotations[daprImageKey])
assert.Equal(t, "true", annotations[daprAppSSLKey])
assert.Equal(t, fmt.Sprintf("%d", maxRequestBodySize), annotations[daprMaxRequestBodySizeKey])
assert.Equal(t, fmt.Sprintf("%d", readBufferSize), annotations[daprReadBufferSizeKey])
assert.Equal(t, "true", annotations[daprHTTPStreamRequestBodyKey])
assert.Equal(t, fmt.Sprintf("%d", gracefulShutdownSeconds), annotations[daprGracefulShutdownSecondsKey])
})
}

View File

@ -0,0 +1,25 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: node
spec:
replicas: 1
selector:
matchLabels:
app: node
template:
metadata:
labels:
app: node
annotations:
dapr.io/enabled: "false"
dapr.io/app-id: "node-app"
spec:
containers:
- name: node
image: dapriosamples/hello-k8s-node:latest
ports:
- containerPort: 3000
imagePullPolicy: Always

View File

@ -0,0 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: node
spec:
replicas: 1
selector:
matchLabels:
app: node
template:
metadata:
labels:
app: node
spec:
containers:
- name: node
image: dapriosamples/hello-k8s-node:latest
ports:
- containerPort: 3000
imagePullPolicy: Always

View File

@ -0,0 +1,26 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: node
spec:
replicas: 1
selector:
matchLabels:
app: node
template:
metadata:
labels:
app: node
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "nodeapp"
dapr.io/app-port: "3000"
spec:
containers:
- name: node
image: dapriosamples/hello-k8s-node:latest
ports:
- containerPort: 3000
imagePullPolicy: Always

View File

@ -0,0 +1,46 @@
apiVersion: v1
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2022-01-14T17:11:56Z"
generation: 1
labels:
app: node
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: node
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: node
spec:
containers:
- image: dapriosamples/hello-k8s-node:latest
imagePullPolicy: Always
name: node
ports:
- containerPort: 3000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
kind: List
metadata: {}

View File

@ -0,0 +1,50 @@
apiVersion: v1
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2022-01-14T17:11:56Z"
generation: 1
labels:
app: node
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: node
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: node
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "nodeapp"
dapr.io/app-port: "3000"
spec:
containers:
- image: dapriosamples/hello-k8s-node:latest
imagePullPolicy: Always
name: node
ports:
- containerPort: 3000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
kind: List
metadata: {}

View File

@ -0,0 +1,47 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
namespace: test
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
namespace: test1
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always

View File

@ -0,0 +1,162 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: appconfig
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: addapp
labels:
app: add
spec:
replicas: 1
selector:
matchLabels:
app: add
template:
metadata:
labels:
app: add
spec:
containers:
- name: add
image: dapriosamples/distributed-calculator-go:latest
ports:
- containerPort: 6000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: divideapp
labels:
app: divide
spec:
replicas: 1
selector:
matchLabels:
app: divide
template:
metadata:
labels:
app: divide
spec:
containers:
- name: divide
image: dapriosamples/distributed-calculator-node:latest
ports:
- containerPort: 4000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: multiplyapp
labels:
app: multiply
spec:
replicas: 1
selector:
matchLabels:
app: multiply
template:
metadata:
labels:
app: multiply
spec:
containers:
- name: multiply
image: dapriosamples/distributed-calculator-python:latest
ports:
- containerPort: 5000
imagePullPolicy: Always
---
kind: Service
apiVersion: v1
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
selector:
app: calculator-front-end
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
replicas: 1
selector:
matchLabels:
app: calculator-front-end
template:
metadata:
labels:
app: calculator-front-end
spec:
containers:
- name: calculator-front-end
image: dapriosamples/distributed-calculator-react-calculator:latest
ports:
- containerPort: 8080
imagePullPolicy: Always
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
# These settings will work out of the box if you use `helm install
# bitnami/redis`. If you have your own setup, replace
# `redis-master:6379` with your own Redis master address, and the
# Redis password with your own Secret's name. For more information,
# see https://docs.dapr.io/operations/components/component-secrets .
- name: redisHost
value: redis-master:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
auth:
secretStore: kubernetes

View File

@ -0,0 +1,166 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: appconfig
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: addapp
labels:
app: add
spec:
replicas: 1
selector:
matchLabels:
app: add
template:
metadata:
labels:
app: add
spec:
containers:
- name: add
image: dapriosamples/distributed-calculator-go:latest
ports:
- containerPort: 6000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: divideapp
labels:
app: divide
spec:
replicas: 1
selector:
matchLabels:
app: divide
template:
metadata:
labels:
app: divide
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "divideapp"
dapr.io/app-port: "4000"
dapr.io/config: "appconfig"
spec:
containers:
- name: divide
image: dapriosamples/distributed-calculator-node:latest
ports:
- containerPort: 4000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: multiplyapp
labels:
app: multiply
spec:
replicas: 1
selector:
matchLabels:
app: multiply
template:
metadata:
labels:
app: multiply
spec:
containers:
- name: multiply
image: dapriosamples/distributed-calculator-python:latest
ports:
- containerPort: 5000
imagePullPolicy: Always
---
kind: Service
apiVersion: v1
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
selector:
app: calculator-front-end
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
replicas: 1
selector:
matchLabels:
app: calculator-front-end
template:
metadata:
labels:
app: calculator-front-end
spec:
containers:
- name: calculator-front-end
image: dapriosamples/distributed-calculator-react-calculator:latest
ports:
- containerPort: 8080
imagePullPolicy: Always
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
# These settings will work out of the box if you use `helm install
# bitnami/redis`. If you have your own setup, replace
# `redis-master:6379` with your own Redis master address, and the
# Redis password with your own Secret's name. For more information,
# see https://docs.dapr.io/operations/components/component-secrets .
- name: redisHost
value: redis-master:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
auth:
secretStore: kubernetes

View File

@ -0,0 +1,186 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: appconfig
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "subtractapp"
dapr.io/app-port: "80"
dapr.io/config: "appconfig"
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: addapp
labels:
app: add
spec:
replicas: 1
selector:
matchLabels:
app: add
template:
metadata:
labels:
app: add
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "addapp"
dapr.io/app-port: "6000"
dapr.io/config: "appconfig"
spec:
containers:
- name: add
image: dapriosamples/distributed-calculator-go:latest
ports:
- containerPort: 6000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: divideapp
labels:
app: divide
spec:
replicas: 1
selector:
matchLabels:
app: divide
template:
metadata:
labels:
app: divide
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "divideapp"
dapr.io/app-port: "4000"
dapr.io/config: "appconfig"
spec:
containers:
- name: divide
image: dapriosamples/distributed-calculator-node:latest
ports:
- containerPort: 4000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: multiplyapp
labels:
app: multiply
spec:
replicas: 1
selector:
matchLabels:
app: multiply
template:
metadata:
labels:
app: multiply
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "multiplyapp"
dapr.io/app-port: "5000"
dapr.io/config: "appconfig"
spec:
containers:
- name: multiply
image: dapriosamples/distributed-calculator-python:latest
ports:
- containerPort: 5000
imagePullPolicy: Always
---
kind: Service
apiVersion: v1
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
selector:
app: calculator-front-end
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
replicas: 1
selector:
matchLabels:
app: calculator-front-end
template:
metadata:
labels:
app: calculator-front-end
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "calculator-front-end"
dapr.io/app-port: "8080"
dapr.io/config: "appconfig"
spec:
containers:
- name: calculator-front-end
image: dapriosamples/distributed-calculator-react-calculator:latest
ports:
- containerPort: 8080
imagePullPolicy: Always
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
# These settings will work out of the box if you use `helm install
# bitnami/redis`. If you have your own setup, replace
# `redis-master:6379` with your own Redis master address, and the
# Redis password with your own Secret's name. For more information,
# see https://docs.dapr.io/operations/components/component-secrets .
- name: redisHost
value: redis-master:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
auth:
secretStore: kubernetes

View File

@ -0,0 +1,166 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: appconfig
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "subtractapp"
dapr.io/app-port: "80"
dapr.io/config: "appconfig"
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: addapp
labels:
app: add
spec:
replicas: 1
selector:
matchLabels:
app: add
template:
metadata:
labels:
app: add
spec:
containers:
- name: add
image: dapriosamples/distributed-calculator-go:latest
ports:
- containerPort: 6000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: divideapp
labels:
app: divide
spec:
replicas: 1
selector:
matchLabels:
app: divide
template:
metadata:
labels:
app: divide
spec:
containers:
- name: divide
image: dapriosamples/distributed-calculator-node:latest
ports:
- containerPort: 4000
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: multiplyapp
labels:
app: multiply
spec:
replicas: 1
selector:
matchLabels:
app: multiply
template:
metadata:
labels:
app: multiply
spec:
containers:
- name: multiply
image: dapriosamples/distributed-calculator-python:latest
ports:
- containerPort: 5000
imagePullPolicy: Always
---
kind: Service
apiVersion: v1
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
selector:
app: calculator-front-end
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: calculator-front-end
labels:
app: calculator-front-end
spec:
replicas: 1
selector:
matchLabels:
app: calculator-front-end
template:
metadata:
labels:
app: calculator-front-end
spec:
containers:
- name: calculator-front-end
image: dapriosamples/distributed-calculator-react-calculator:latest
ports:
- containerPort: 8080
imagePullPolicy: Always
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
# These settings will work out of the box if you use `helm install
# bitnami/redis`. If you have your own setup, replace
# `redis-master:6379` with your own Redis master address, and the
# Redis password with your own Secret's name. For more information,
# see https://docs.dapr.io/operations/components/component-secrets .
- name: redisHost
value: redis-master:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
auth:
secretStore: kubernetes

View File

@ -0,0 +1,50 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
namespace: test
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: subtractapp
namespace: test1
labels:
app: subtract
spec:
replicas: 1
selector:
matchLabels:
app: subtract
template:
metadata:
labels:
app: subtract
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "subtractapp"
spec:
containers:
- name: subtract
image: dapriosamples/distributed-calculator-csharp:latest
ports:
- containerPort: 80
imagePullPolicy: Always

View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: Pod
metadata:
name: mypod
namespace: test
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

View File

@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
name: nginx
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "test-app"
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

View File

@ -0,0 +1,18 @@
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
name: nginx
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "test-app"
dapr.io/enable-profiling: "true"
dapr.io/log-level: "info"
dapr.io/sidecar-image: "custom-image"
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: mypod
labels:
name: nginx
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "default-pod-mypod"
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: mypod
namespace: test
labels:
name: nginx
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "test-pod-mypod"
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80