├── LICENSE ├── .gitignore ├── cmd ├── examples │ ├── v1 │ │ ├── namespace.yaml │ │ ├── configmap.yaml │ │ ├── serviceaccount.yaml │ │ ├── pod.yaml │ │ └── service.yaml │ ├── gateway.networking.k8s.io │ │ └── v1beta1 │ │ │ └── gateway.yaml │ ├── security.istio.io │ │ └── v1beta1 │ │ │ ├── requestauthentication.yaml │ │ │ └── authorizationpolicy.yaml │ ├── apps │ │ └── v1 │ │ │ └── deployment.yaml │ └── networking.istio.io │ │ └── v1beta1 │ │ ├── destinationrule.yaml │ │ ├── virtualservice.yaml │ │ └── gateway.yaml ├── root.go └── examples.go ├── main.go ├── go.mod ├── Makefile ├── .github └── workflows │ └── ci.yaml ├── go.sum └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | -------------------------------------------------------------------------------- /cmd/examples/v1/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: STRING 5 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2023 NAME HERE 3 | 4 | */ 5 | package main 6 | 7 | import "github.com/trstringer/kubectl-example/cmd" 8 | 9 | func main() { 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/trstringer/kubectl-example 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 7 | github.com/spf13/cobra v1.6.1 // indirect 8 | github.com/spf13/pflag v1.0.5 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /cmd/examples/v1/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | data: 7 | # Single line data. 8 | STRING: STRING 9 | # Multiline data. 10 | STRING: | 11 | STRING 12 | STRING 13 | -------------------------------------------------------------------------------- /cmd/examples/v1/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | automountServiceAccountToken: TRUE 7 | # Reference image pull secrets for pods associated with this service account. 8 | imagePullSecrets: 9 | - name: STRING 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DESTINATION_DIR=$$HOME/.local/bin 2 | 3 | .PHONY: build 4 | build: 5 | mkdir -p ./bin 6 | go build -o ./bin/kubectl-example 7 | 8 | .PHONY: install 9 | install: build 10 | cp ./bin/kubectl-example $(DESTINATION_DIR) 11 | 12 | .PHONY: lint 13 | lint: 14 | docker run --rm -v $$(pwd):/app -w /app golangci/golangci-lint:v1.46.2 golangci-lint run -v 15 | -------------------------------------------------------------------------------- /cmd/examples/v1/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | containers: 8 | - name: STRING 9 | image: STRING 10 | imagePullPolicy: STRING # Always, IfNotPresent, Never 11 | command: ["STRING"] 12 | args: 13 | - STRING 14 | ports: 15 | - containerPort: 1234 16 | -------------------------------------------------------------------------------- /cmd/examples/gateway.networking.k8s.io/v1beta1/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1beta1 2 | kind: Gateway 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | gatewayClassName: STRING 8 | listeners: 9 | - name: STRING 10 | hostname: STRING 11 | port: 1234 12 | protocol: STRING 13 | allowedRoutes: 14 | namespaces: 15 | from: STRING 16 | -------------------------------------------------------------------------------- /cmd/examples/security.istio.io/v1beta1/requestauthentication.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: security.istio.io/v1beta1 2 | kind: RequestAuthentication 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | jwtRules: 8 | - issuer: STRING # Required, JWT issuer from `iss` claim 9 | jwksUri: STRING # Public key set URL (either jwksUri or jwks should be set) 10 | jwks: STRING # JSON key set 11 | selector: # Optional, workload selector 12 | matchLabels: 13 | STRING: STRING 14 | -------------------------------------------------------------------------------- /cmd/examples/v1/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | type: STRING # ClusterIP (default), NodePort, LoadBalancer, ExternalName 8 | selector: 9 | STRING: STRING 10 | ports: 11 | - name: STRING # Optional if only a single port 12 | port: 1234 # Port exposed by service 13 | protocol: STRING # TCP (default), UDP, SCTP 14 | targetPort: 1234 15 | externalName: STRING # Only when spec.type is ExternalName 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | paths-ignore: 9 | - '**/*.md' 10 | pull_request: 11 | branches: 12 | - main 13 | paths-ignore: 14 | - '**/*.md' 15 | 16 | jobs: 17 | ci: 18 | name: CI 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v2 23 | - name: Build 24 | run: make build 25 | env: 26 | VERSION: latest 27 | - name: Lint 28 | run: make lint 29 | -------------------------------------------------------------------------------- /cmd/examples/apps/v1/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | replicas: 1234 8 | selector: 9 | matchLabels: 10 | STRING: STRING 11 | template: 12 | metadata: 13 | labels: 14 | STRING: STRING 15 | spec: 16 | containers: 17 | - name: STRING 18 | image: STRING 19 | imagePullPolicy: STRING # Always, IfNotPresent, Never 20 | command: ["STRING"] 21 | args: 22 | - STRING 23 | ports: 24 | - containerPort: 1234 25 | -------------------------------------------------------------------------------- /cmd/examples/networking.istio.io/v1beta1/destinationrule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1beta1 2 | kind: DestinationRule 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | host: STRING # Service name from service registry 8 | subsets: 9 | - name: STRING 10 | labels: 11 | STRING: STRING 12 | trafficPolicy: 13 | loadBalancer: 14 | # One of simple or consistentHash 15 | simple: STRING # ROUND_ROBIN, LEAST_REQUEST, PASSTHROUGH, RANDOM, UNSPECIFIED 16 | consistentHash: 17 | # One of the following 18 | httpHeaderName: STRING 19 | httpCookie: 20 | name: STRING 21 | ttl: 0s 22 | useSourceIp: TRUE 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 2 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 3 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 4 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 5 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 6 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 7 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 8 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /cmd/examples/networking.istio.io/v1beta1/virtualservice.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1beta1 2 | kind: VirtualService 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | hosts: 8 | - STRING 9 | # Gateways bound to, format `namespace/gateway`. If omitted it is 10 | # the `mesh` gateway. If no namespace, it uses the VirtualService 11 | # namespace. 12 | gateways: 13 | - STRING 14 | http: 15 | - name: STRING 16 | # All match blocks are AND'd 17 | match: 18 | - uri: 19 | prefix: STRING 20 | - uri: 21 | exact: STRING 22 | - uri: 23 | regex: STRING 24 | route: 25 | - destination: 26 | host: STRING 27 | subset: STRING 28 | port: 29 | number: 1234 30 | weight: 1234 31 | rewrite: 32 | uri: STRING 33 | # Delegate to a different VirtualService. Exclusive from using a 34 | # spec.http.route for a destination. 35 | delegate: 36 | name: STRING 37 | namespace: STRING 38 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2023 Thomas Stringer 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "strings" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var list bool 15 | 16 | // rootCmd represents the base command when called without any subcommands 17 | var rootCmd = &cobra.Command{ 18 | Use: "kubectl-example", 19 | Short: "Show example manifests for resources", 20 | Run: func(cmd *cobra.Command, args []string) { 21 | if list { 22 | resourceNames, err := getResources() 23 | if err != nil { 24 | fmt.Printf("Error getting resource names: %v\n", err) 25 | os.Exit(1) 26 | } 27 | fmt.Println(strings.Join(resourceNames, "\n")) 28 | os.Exit(0) 29 | } 30 | 31 | if len(args) == 0 { 32 | fmt.Fprintf(os.Stderr, "Pass in a resource to show or `--list`") 33 | os.Exit(1) 34 | } 35 | 36 | definitions, err := getResourcesByName(args) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "Error getting definitions: %v\n", err) 39 | os.Exit(1) 40 | } 41 | fmt.Print(strings.Join(definitions, "---\n")) 42 | }, 43 | } 44 | 45 | func Execute() { 46 | err := rootCmd.Execute() 47 | if err != nil { 48 | os.Exit(1) 49 | } 50 | } 51 | 52 | func init() { 53 | rootCmd.Flags().BoolVarP(&list, "list", "l", false, "list available resources") 54 | } 55 | -------------------------------------------------------------------------------- /cmd/examples/security.istio.io/v1beta1/authorizationpolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: security.istio.io/v1beta1 2 | kind: AuthorizationPolicy 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | action: STRING # ALLOW (default), DENY, AUDIT, CUSTOM 8 | selector: # Optional, workload selector 9 | matchLabels: 10 | STRING: STRING 11 | rules: 12 | - from: 13 | - source: 14 | principals: ["STRING"] # mTLS peer identities, format `/ns//sa/` 15 | notPrincipals: ["STRING"] 16 | requestPrincipals: ["STRING"] # Request identities from JWT, format `iss/sub` 17 | notRequestPrincipals: ["STRING"] 18 | namespaces: ["STRING"] # mTLS namespaces (if unset, any namespace allowed) 19 | notNamespaces: ["STRING"] 20 | to: 21 | - operation: 22 | paths: ["STRING"] 23 | notPaths: ["STRING"] 24 | methods: ["STRING"] 25 | notMethods: ["STRING"] 26 | hosts: ["STRING"] 27 | notHosts: ["STRING"] 28 | ports: ["STRING"] 29 | notPorts: ["STRING"] 30 | when: 31 | - key: STRING # Supported conditions: https://istio.io/latest/docs/reference/config/security/conditions 32 | values: ["STRING"] 33 | notValues: ["STRING"] 34 | -------------------------------------------------------------------------------- /cmd/examples/networking.istio.io/v1beta1/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1beta1 2 | kind: Gateway 3 | metadata: 4 | name: STRING 5 | namespace: STRING 6 | spec: 7 | selector: 8 | STRING: STRING # Gateway pod label selector 9 | servers: 10 | - hosts: 11 | - STRING 12 | port: 13 | number: 1234 14 | name: STRING 15 | protocol: STRING # HTTP, HTTPS, GRPC, HTTP2, MONGO, TCP, TLS 16 | tls: 17 | httpsRedirect: TRUE # Not required, if true sends 301 for HTTP requests 18 | mode: STRING # PASSTHROUGH, SIMPLE, MUTUAL, AUTO_PASSTHROUGH, ISTIO_MUTUAL 19 | # For key and certs, if using MUTUAL or SIMPLE you need to pass in 20 | # the server certificate and private key as either file paths, or 21 | # within a credentialName, which is a generic secret stored in istio-system 22 | # namespace with the data values `key`, `cert`, and with MUTUAL `cacert`. 23 | serverCertificate: STRING # File path. Required if mode is SIMPLE or MUTUAL, and credentialName not specified 24 | privateKey: STRING # File path. Required if mode is SIMPLE or MUTUAL, and credentialName not specified 25 | caCertificates: STRING # File path. Required if mode is MUTUAL, and credentialName not specified 26 | credentialName: STRING # istio-system secret name, replaces serverCertificate, privateKey, and caCertificates 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kubectl example 2 | 3 | Quickly and easily create example manifests so you don't have to remember Kubernetes resource manifest syntax. 4 | 5 | ## Installation 6 | 7 | ``` 8 | $ make install 9 | ``` 10 | 11 | *Note: This installs the plugin in ~/.local/bin. If you prefer to use a different directory in your `$PATH` then modify the `Makefile` prior to running `make install`.* 12 | 13 | Verify installation: 14 | 15 | ``` 16 | $ kubectl example --help 17 | Show example manifests for resources 18 | 19 | Usage: 20 | kubectl-example [flags] 21 | 22 | Flags: 23 | -h, --help help for kubectl-example 24 | -l, --list list available resources 25 | ``` 26 | 27 | ## Usage 28 | 29 | List out all available example manifests: 30 | 31 | ``` 32 | $ kubectl example --list 33 | deployment 34 | gateway [gateway.networking.k8s.io/v1beta1 networking.istio.io/v1beta1] 35 | namespace 36 | pod 37 | service 38 | virtualservice 39 | ``` 40 | 41 | *Note: This list will continue to grow as I add more example manifests. PRs welcome and appreciated!* 42 | 43 | Dump out a deployment example manifest: 44 | 45 | ``` 46 | $ kubectl example deployment 47 | apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: STRING 51 | namespace: STRING 52 | spec: 53 | replicas: 1234 54 | selector: 55 | matchLabels: 56 | STRING: STRING 57 | template: 58 | metadata: 59 | labels: 60 | STRING: STRING 61 | spec: 62 | containers: 63 | - name: STRING 64 | image: STRING 65 | imagePullPolicy: STRING # Always, IfNotPresent, Never 66 | ports: 67 | - containerPort: 1234 68 | ``` 69 | 70 | You can also dump multiple manifests at the same time: 71 | 72 | ``` 73 | $ kubectl example namespace pod 74 | apiVersion: v1 75 | kind: Namespace 76 | metadata: 77 | name: STRING 78 | --- 79 | apiVersion: v1 80 | kind: Pod 81 | metadata: 82 | name: STRING 83 | namespace: STRING 84 | spec: 85 | containers: 86 | - name: STRING 87 | image: STRING 88 | imagePullPolicy: STRING # Always, IfNotPresent, Never 89 | ports: 90 | - containerPort: 1234 91 | ``` 92 | 93 | Then, just modify the placeholders to your desired values, delete unwanted sections, add whatever you want and create these resources in your Kubernetes clusters! 94 | -------------------------------------------------------------------------------- /cmd/examples.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "path/filepath" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | //go:embed examples/* 12 | var examples embed.FS 13 | 14 | const examplesDir string = "examples" 15 | 16 | func resourceNameFromFile(filename string) string { 17 | fileParts := strings.Split(filename, "/") 18 | fileName := strings.Split(fileParts[len(fileParts)-1], ".")[0] 19 | return fileName 20 | } 21 | 22 | func resourcePathFromDir(dir string) string { 23 | dirParts := strings.Split(dir, "/") 24 | if dirParts[0] != examplesDir { 25 | return dir 26 | } 27 | return strings.Join(dirParts[1:], "/") 28 | } 29 | 30 | func getResourcesByDir(dir string) (map[string][]string, error) { 31 | resourceMap := make(map[string][]string) 32 | files, err := examples.ReadDir(dir) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | for _, file := range files { 38 | if file.IsDir() { 39 | subResourceMap, err := getResourcesByDir(filepath.Join(dir, file.Name())) 40 | if err != nil { 41 | return nil, fmt.Errorf("error reading dir %s: %v", file.Name(), err) 42 | } 43 | for resource, dirs := range subResourceMap { 44 | resourceMap[resource] = append(resourceMap[resource], dirs...) 45 | } 46 | } else { 47 | resourceName := resourceNameFromFile(file.Name()) 48 | resourceMap[resourceName] = append(resourceMap[resourceName], resourcePathFromDir(dir)) 49 | } 50 | } 51 | 52 | return resourceMap, nil 53 | } 54 | 55 | func getResources() ([]string, error) { 56 | resourceMap, err := getResourcesByDir(examplesDir) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | resourceList := []string{} 62 | for resource, dirs := range resourceMap { 63 | if len(dirs) == 1 { 64 | resourceList = append(resourceList, resource) 65 | continue 66 | } 67 | 68 | sort.Strings(dirs) 69 | resourceList = append(resourceList, fmt.Sprintf("%s %v", resource, dirs)) 70 | } 71 | 72 | sort.Strings(resourceList) 73 | return resourceList, nil 74 | } 75 | 76 | func getResourcesByName(names []string) ([]string, error) { 77 | definitions := []string{} 78 | resourceMap, err := getResourcesByDir(examplesDir) 79 | if err != nil { 80 | return nil, fmt.Errorf("error getting resources by dir: %w", err) 81 | } 82 | 83 | for _, name := range names { 84 | nameParts := strings.Split(name, "/") 85 | kind := nameParts[len(nameParts)-1] 86 | groupVersion := strings.Join(nameParts[:len(nameParts)-1], "/") 87 | 88 | kindGroupVersions, found := resourceMap[kind] 89 | if !found { 90 | return nil, fmt.Errorf("kind %s not found", name) 91 | } 92 | if len(kindGroupVersions) > 1 && groupVersion == "" { 93 | return nil, fmt.Errorf("ambigious kind %s, specify a group version prefix: %v", kind, kindGroupVersions) 94 | } 95 | 96 | if groupVersion == "" && len(kindGroupVersions) == 1 { 97 | groupVersion = kindGroupVersions[0] 98 | } 99 | 100 | definitionPath := filepath.Join(examplesDir, groupVersion, kind) 101 | definitionPath += ".yaml" 102 | contents, err := examples.ReadFile(definitionPath) 103 | if err != nil { 104 | return nil, fmt.Errorf("error reading path %s: %w", definitionPath, err) 105 | } 106 | definitions = append(definitions, string(contents)) 107 | } 108 | 109 | return definitions, nil 110 | } 111 | --------------------------------------------------------------------------------