...

Source file src/github.com/datawire/ambassador/v2/cmd/k8sregistryctl/main.go

Documentation: github.com/datawire/ambassador/v2/cmd/k8sregistryctl

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"text/template"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/spf13/cobra"
    15  
    16  	"github.com/datawire/ambassador/v2/pkg/k8s"
    17  	"github.com/datawire/ambassador/v2/pkg/kubeapply"
    18  )
    19  
    20  var tmpl = template.Must(template.
    21  	New("docker-registry.yaml").
    22  	Parse(`---
    23  apiVersion: v1
    24  kind: Namespace
    25  metadata:
    26    name: docker-registry
    27  ---
    28  apiVersion: v1
    29  kind: Service
    30  metadata:
    31    namespace: docker-registry
    32    name: registry
    33  spec:
    34    type: NodePort
    35    selector:
    36      app: registry
    37    ports:
    38      - port: 5000
    39        nodePort: 31000
    40  ---
    41  apiVersion: apps/v1
    42  # XXX: Avoid using a StatefulSet if possible, because kubeapply
    43  # doesn't know how to wait for them.
    44  kind: {{ if eq .Storage "pvc" }}StatefulSet{{ else }}Deployment{{ end }}
    45  metadata:
    46    namespace: docker-registry
    47    name: registry
    48  spec:
    49    replicas: 1
    50  {{ if eq .Storage "pvc" }} # XXX: StatefulSet
    51    serviceName: registry
    52  {{ end }}
    53    selector:
    54      matchLabels:
    55        app: registry
    56    template:
    57      metadata:
    58        name: registry
    59        labels:
    60          app: registry
    61      spec:
    62        containers:
    63          - name: registry
    64            image: docker.io/library/registry:2
    65            ports:
    66              - containerPort: 5000
    67            volumeMounts:
    68              - mountPath: /var/lib/registry
    69                name: registry-data
    70        volumes:
    71          - name: registry-data
    72  {{ if eq .Storage "pvc" | not }}
    73            # On Kubeception clusters, there is only 1 node, so a
    74            # hostPath is fine.
    75            hostPath:
    76              path: /var/lib/registry
    77  {{ else }}
    78            persistentVolumeClaim:
    79              claimName: registry-data
    80  ---
    81  apiVersion: v1
    82  kind: PersistentVolumeClaim
    83  metadata:
    84    name: registry-data
    85    namespace: docker-registry
    86  spec:
    87    accessModes:
    88      - ReadWriteOnce
    89    resources:
    90      requests:
    91        storage: 10Gi
    92  {{ end }}
    93  `))
    94  
    95  func main() {
    96  	argparser := &cobra.Command{
    97  		Use:           os.Args[0],
    98  		Short:         "Manage a private in-cluster registry",
    99  		SilenceErrors: true,
   100  		SilenceUsage:  true,
   101  	}
   102  	argparser.AddCommand(func() *cobra.Command {
   103  		var (
   104  			argStorage string
   105  		)
   106  		subparser := &cobra.Command{
   107  			Use:   "up",
   108  			Short: "Initialize the registry, and create a port-forward to it",
   109  			Args:  cobra.ExactArgs(0),
   110  			RunE: func(cobraCmd *cobra.Command, _ []string) error {
   111  				var kpfTarget string
   112  				switch argStorage {
   113  				case "pvc":
   114  					kpfTarget = "statefulset/registry"
   115  				case "hostPath":
   116  					kpfTarget = "deployment/registry"
   117  				default:
   118  					return errors.Errorf("invalid --storage=%q: must be one of 'pvc' or 'hostPath'", argStorage)
   119  				}
   120  
   121  				kubeinfo := k8s.NewKubeInfo("", "", "")
   122  
   123  				// Part 1: Apply the YAML
   124  				//
   125  				// pkg/kubeapply is annoyingly oriented around actual physical files >:(
   126  				tmpdir, err := ioutil.TempDir("", filepath.Base(os.Args[0]))
   127  				if err != nil {
   128  					return err
   129  				}
   130  				defer os.RemoveAll(tmpdir)
   131  				yamlFile, err := os.OpenFile(filepath.Join(tmpdir, "docker-registry.yaml"), os.O_CREATE|os.O_WRONLY, 0600)
   132  				if err != nil {
   133  					return err
   134  				}
   135  				err = tmpl.Execute(yamlFile, map[string]interface{}{
   136  					"Storage": argStorage,
   137  				})
   138  				yamlFile.Close()
   139  				if err != nil {
   140  					return err
   141  				}
   142  				err = kubeapply.Kubeapply(
   143  					cobraCmd.Context(), // context
   144  					kubeinfo,           // kubeinfo
   145  					time.Minute,        // perPhaseTimeout
   146  					false,              // debug
   147  					false,              // dryRun
   148  					yamlFile.Name(),    // files
   149  				)
   150  				if err != nil {
   151  					return err
   152  				}
   153  
   154  				// Part 2: Set up the port-forward
   155  				args, err := kubeinfo.GetKubectlArray("port-forward",
   156  					"--namespace=docker-registry",
   157  					kpfTarget,
   158  					"31000:5000")
   159  				if err != nil {
   160  					return err
   161  				}
   162  				cmd := exec.Command("kubectl", args...)
   163  				cmd.Stdout, err = os.OpenFile(filepath.Join(os.TempDir(), filepath.Base(os.Args[0])+".log"), os.O_CREATE|os.O_WRONLY, 0666)
   164  				if err != nil {
   165  					return err
   166  				}
   167  				cmd.Stderr = cmd.Stdout
   168  				if err := cmd.Start(); err != nil {
   169  					return err
   170  				}
   171  				for {
   172  					_, httpErr := http.Get("http://localhost:31000/")
   173  					if httpErr == nil {
   174  						fmt.Fprintln(os.Stderr, "port-forward ready")
   175  						break
   176  					} else {
   177  						fmt.Fprintln(os.Stderr, "waiting for port-forward to become ready...")
   178  						time.Sleep(time.Second)
   179  					}
   180  				}
   181  				return nil
   182  			},
   183  		}
   184  		subparser.Flags().StringVar(&argStorage, "storage", "", "Which type of storage to use ('pvc' or 'hostPath')")
   185  		return subparser
   186  	}())
   187  	argparser.AddCommand(&cobra.Command{
   188  		Use:   "down",
   189  		Short: "Shut down the port-forward to the registry",
   190  		Args:  cobra.ExactArgs(0),
   191  		RunE: func(_ *cobra.Command, _ []string) error {
   192  			cmd := exec.Command("killall", "kubectl") // XXX
   193  			cmd.Stdout = os.Stderr
   194  			cmd.Stderr = os.Stderr
   195  			return cmd.Run()
   196  		},
   197  	})
   198  	if err := argparser.Execute(); err != nil {
   199  		fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err)
   200  		os.Exit(1)
   201  	}
   202  }
   203  

View as plain text