Skip to content

Commit

Permalink
Add CLI command to generate k8s manifests (jaegertracing#1046)
Browse files Browse the repository at this point in the history
* Add CLI command jaeger-operator generate to generate manifests

Defaults to stdin/stdout

Examples:

   jaeger-operator generate --jaeger-all-in-one-image localimage --cr ./deploy/examples/all-in-one-with-options.yaml | kubectl apply -f -

   cat jaeger.yaml | podman run jaeger-operator generate > manifest.yaml

Fixes jaegertracing#375

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Add missing TypeMeta for corev1.ServiceAccount

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Add e2e tests for `jaeger-operator generate`

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Avoid command line flag duplication

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Fixup - allow EOF on stdin

Signed-off-by: Carl Henrik Lunde <[email protected]>

* README: Explain generate subcommand

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Generate command description: update

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Strategy: Test that All includes all types

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Remove solved TODOs

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Use AllInOneSmokeTest for e2e CLI generate

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Fix tag name in README

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Update docs

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Use require.No* for better error message in e2e test

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Update docs, mark as experimental

Signed-off-by: Carl Henrik Lunde <[email protected]>

* Minor refactor/cleanup

Signed-off-by: Carl Henrik Lunde <[email protected]>

Co-authored-by: Carl Henrik Lunde <[email protected]>
  • Loading branch information
chlunde and chlunde authored May 12, 2020
1 parent 911f3e6 commit f3f1f23
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 22 deletions.
4 changes: 4 additions & 0 deletions .ci/run-e2e-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ elif [ "${TEST_GROUP}" = "es-token-propagation" ]
then
echo "Running token propagation tests"
make e2e-tests-token-propagation-es
elif [ "${TEST_GROUP}" = "generate" ]
then
echo "Running CLI manifest generatation tests"
make e2e-tests-generate
else
echo "Unknown TEST_GROUP [${TEST_GROUP}]"; exit 1
fi
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/e2e-kubernetes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-16.04
strategy:
matrix:
TEST_GROUP: [smoke, es, cassandra, streaming, examples1, examples2]
TEST_GROUP: [smoke, es, cassandra, streaming, examples1, examples2, generate]
steps:
- uses: actions/setup-go@v1
with:
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ unit-tests:
@go test $(VERBOSE) $(UNIT_TEST_PACKAGES) -cover -coverprofile=cover.out -ldflags $(LD_FLAGS)

.PHONY: e2e-tests
e2e-tests: prepare-e2e-tests e2e-tests-smoke e2e-tests-cassandra e2e-tests-es e2e-tests-self-provisioned-es e2e-tests-streaming e2e-tests-examples1 e2e-tests-examples2 e2e-tests-examples-openshift
e2e-tests: prepare-e2e-tests e2e-tests-smoke e2e-tests-cassandra e2e-tests-es e2e-tests-self-provisioned-es e2e-tests-streaming e2e-tests-examples1 e2e-tests-examples2 e2e-tests-examples-openshift e2e-tests-generate

.PHONY: prepare-e2e-tests
prepare-e2e-tests: build docker push
Expand All @@ -109,6 +109,11 @@ e2e-tests-smoke: prepare-e2e-tests
@echo Running Smoke end-to-end tests...
@BUILD_IMAGE=$(BUILD_IMAGE) go test -tags=smoke ./test/e2e/... $(TEST_OPTIONS)

.PHONY: e2e-tests-generate
e2e-tests-generate: prepare-e2e-tests
@echo Running generate end-to-end tests...
@BUILD_IMAGE=$(BUILD_IMAGE) go test -tags=generate ./test/e2e/... $(TEST_OPTIONS)

.PHONY: e2e-tests-cassandra
e2e-tests-cassandra: prepare-e2e-tests cassandra
@echo Running Cassandra end-to-end tests...
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ In this example, the Jaeger UI is available at http://192.168.122.34.

The official documentation for the Jaeger Operator, including all its customization options, are available under the main [Jaeger Documentation](https://www.jaegertracing.io/docs/latest/operator/).


## (experimental) Generate Kubernetes manifest file

Sometimes it is preferable to generate plain manifests files instead of running an operator in a cluster. `jaeger-operator generate` generates kubernetes manifests from a given CR. In this example we apply the manifest generated by [examples/simplest.yaml](https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/examples/simplest.yaml) to the namespace `jaeger-test`:

```bash
curl https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/examples/simplest.yaml | docker run --rm jaegertracing/jaeger-operator:master generate | kubectl apply -n jaeger-test -f -
```

It is recommended to deploy the operator instead of generating a static manifest.

## Contributing and Developing

Please see [CONTRIBUTING.md](CONTRIBUTING.md).
Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/jaegertracing/jaeger-operator/pkg/cmd/generate"
"github.com/jaegertracing/jaeger-operator/pkg/cmd/start"
"github.com/jaegertracing/jaeger-operator/pkg/cmd/version"
)
Expand Down Expand Up @@ -37,6 +38,7 @@ func init() {

RootCmd.AddCommand(start.NewStartCommand())
RootCmd.AddCommand(version.NewVersionCommand())
RootCmd.AddCommand(generate.NewGenerateCommand())
}

// initConfig reads in config file and ENV variables if set.
Expand Down
4 changes: 4 additions & 0 deletions pkg/account/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func Get(jaeger *v1.Jaeger) []*corev1.ServiceAccount {
func getMain(jaeger *v1.Jaeger) *corev1.ServiceAccount {
trueVar := true
return &corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ServiceAccount",
},
ObjectMeta: metav1.ObjectMeta{
Name: JaegerServiceAccountFor(jaeger, ""),
Namespace: jaeger.Namespace,
Expand Down
111 changes: 111 additions & 0 deletions pkg/cmd/generate/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package generate

import (
"context"
"fmt"
"io"
"os"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/util/yaml"

v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/cmd/start"
"github.com/jaegertracing/jaeger-operator/pkg/strategy"
)

// NewGenerateCommand starts the Jaeger Operator
func NewGenerateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "generate",
Short: "(experimental) Generate YAML manifests from Jaeger CRD",
Long: `Generate YAML manifests from Jaeger CRD.
Defaults to reading Jaeger CRD from standard input and writing the manifest file to standard output, override with --cr <filename> and --output <filename>.`,
RunE: generate,
}

cmd.Flags().String("cr", "/dev/stdin", "Input Jaeger CRD")
viper.BindPFlag("cr", cmd.Flags().Lookup("cr"))

cmd.Flags().String("output", "/dev/stdout", "Where to print the generated YAML documents")
viper.BindPFlag("output", cmd.Flags().Lookup("output"))

start.AddFlags(cmd)

return cmd
}

func createSpecFromYAML(filename string) (*v1.Jaeger, error) {
// #nosec G304: Potential file inclusion via variable
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()

var spec v1.Jaeger
decoder := yaml.NewYAMLOrJSONDecoder(f, 8192)
if err := decoder.Decode(&spec); err != nil && err != io.EOF {
return nil, err
}

return &spec, nil
}

func generate(_ *cobra.Command, _ []string) error {
level, err := log.ParseLevel(viper.GetString("log-level"))
if err != nil {
log.SetLevel(log.InfoLevel)
} else {
log.SetLevel(level)
}

input := viper.GetString("cr")
if input == "/dev/stdin" {
// Reading from stdin by default is neat when running as a
// container instead of a binary, but is confusing when no
// input is sent and the program just hangs
log.Info("Reading Jaeger CRD from standard input (use --cr <filename> to override)")
}

spec, err := createSpecFromYAML(input)
if err != nil {
return err
}

s := strategy.For(context.Background(), spec)

outputName := viper.GetString("output")
out, err := os.OpenFile(outputName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
return err
}

defer out.Close()

encoder := k8s_json.NewYAMLSerializer(k8s_json.DefaultMetaFactory, nil, nil)
for _, obj := range s.All() {
// OwnerReferences normally references the CR, but it is not a
// resource in the cluster so we must remove it

type f interface {
SetOwnerReferences(references []metav1.OwnerReference)
}

meta := obj.(f)
meta.SetOwnerReferences(nil)

fmt.Fprintln(out, "---")
if err := encoder.Encode(obj, out); err != nil {
log.Fatal(err)
}
}

return nil
}
46 changes: 27 additions & 19 deletions pkg/cmd/start/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,12 @@ import (
"github.com/jaegertracing/jaeger-operator/pkg/version"
)

// NewStartCommand starts the Jaeger Operator
func NewStartCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Starts a new Jaeger Operator",
Long: "Starts a new Jaeger Operator",
Run: func(cmd *cobra.Command, args []string) {
start(cmd, args)
},
}
// AddFlags adds all command line flags related to manifest
// generation. They are shared between the operator and CLI `generate`
// command.
func AddFlags(cmd *cobra.Command) {
cmd.Flags().String("log-level", "info", "The log-level for the operator. Possible values: trace, debug, info, warning, error, fatal, panic")
viper.BindPFlag("log-level", cmd.Flags().Lookup("log-level"))

cmd.Flags().String("jaeger-version", version.DefaultJaeger(), "Deprecated: the Jaeger version is now managed entirely by the operator. This option is currently no-op.")

Expand Down Expand Up @@ -70,9 +66,28 @@ func NewStartCommand() *cobra.Command {
cmd.Flags().String("kafka-provision", "auto", "Whether to auto-provision a Kafka cluster for suitable Jaeger instances. Possible values: 'yes', 'no', 'auto'. When set to 'auto' and the API name 'kafka.strimzi.io' is available, auto-provisioning is enabled.")
viper.BindPFlag("kafka-provision", cmd.Flags().Lookup("kafka-provision"))

cmd.Flags().String("log-level", "info", "The log-level for the operator. Possible values: trace, debug, info, warning, error, fatal, panic")
viper.BindPFlag("log-level", cmd.Flags().Lookup("log-level"))
docURL := fmt.Sprintf("https://www.jaegertracing.io/docs/%s", version.DefaultJaegerMajorMinor())
cmd.Flags().String("documentation-url", docURL, "The URL for the 'Documentation' menu item")
viper.BindPFlag("documentation-url", cmd.Flags().Lookup("documentation-url"))

cmd.Flags().Bool("kafka-provisioning-minimal", false, "(unsupported) Whether to provision Kafka clusters with minimal requirements, suitable for demos and tests.")
viper.BindPFlag("kafka-provisioning-minimal", cmd.Flags().Lookup("kafka-provisioning-minimal"))
}

// NewStartCommand starts the Jaeger Operator
func NewStartCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Starts a new Jaeger Operator",
Long: "Starts a new Jaeger Operator",
Run: func(cmd *cobra.Command, args []string) {
start(cmd, args)
},
}

AddFlags(cmd)

// Operator specific flags here. Any flags affecting manifest generation should be added to AddFlags instead
cmd.Flags().String("metrics-host", "0.0.0.0", "The host to bind the metrics port")
viper.BindPFlag("metrics-host", cmd.Flags().Lookup("metrics-host"))

Expand All @@ -82,19 +97,12 @@ func NewStartCommand() *cobra.Command {
cmd.Flags().Int32("cr-metrics-port", 8686, "The metrics port for Operator and/or Custom Resource based metrics")
viper.BindPFlag("cr-metrics-port", cmd.Flags().Lookup("cr-metrics-port"))

docURL := fmt.Sprintf("https://www.jaegertracing.io/docs/%s", version.DefaultJaegerMajorMinor())
cmd.Flags().String("documentation-url", docURL, "The URL for the 'Documentation' menu item")
viper.BindPFlag("documentation-url", cmd.Flags().Lookup("documentation-url"))

cmd.Flags().String("jaeger-agent-hostport", "localhost:6831", "The location for the Jaeger Agent")
viper.BindPFlag("jaeger-agent-hostport", cmd.Flags().Lookup("jaeger-agent-hostport"))

cmd.Flags().Bool("tracing-enabled", false, "Whether the Operator should report its own spans to a Jaeger instance")
viper.BindPFlag("tracing-enabled", cmd.Flags().Lookup("tracing-enabled"))

cmd.Flags().Bool("kafka-provisioning-minimal", false, "(unsupported) Whether to provision Kafka clusters with minimal requirements, suitable for demos and tests.")
viper.BindPFlag("kafka-provisioning-minimal", cmd.Flags().Lookup("kafka-provisioning-minimal"))

return cmd
}

Expand Down
74 changes: 73 additions & 1 deletion pkg/strategy/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime"

v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
kafkav1beta1 "github.com/jaegertracing/jaeger-operator/pkg/apis/kafka/v1beta1"
Expand All @@ -17,7 +18,8 @@ import (

// S knows what type of deployments to build based on a given spec
type S struct {
typ v1.DeploymentStrategy
typ v1.DeploymentStrategy
// When adding a new type here, remember to update All() too
accounts []corev1.ServiceAccount
clusterRoleBindings []rbac.ClusterRoleBinding
configMaps []corev1.ConfigMap
Expand Down Expand Up @@ -209,3 +211,73 @@ func (s S) Secrets() []corev1.Secret {
func (s S) Dependencies() []batchv1.Job {
return s.dependencies
}

// All returns the list of all objects for this strategy
func (s S) All() []runtime.Object {
var ret []runtime.Object

// Keep ordering close to
// https://github.com/kubernetes-sigs/kustomize/blob/master/api/resid/gvk.go#L77-L103

for _, o := range s.accounts {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.clusterRoleBindings {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.configMaps {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.cronJobs {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.elasticsearches {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.ingresses {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.horizontalPodAutoscalers {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.kafkas {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.kafkaUsers {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.routes {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.services {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.secrets {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.dependencies {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.daemonSets {
ret = append(ret, o.DeepCopy())
}

for _, o := range s.deployments {
ret = append(ret, o.DeepCopy())
}

return ret
}
Loading

0 comments on commit f3f1f23

Please sign in to comment.