Skip to content

Commit

Permalink
Add support for Load Balancers and Certificates (#245)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas Kämmerling <[email protected]>
  • Loading branch information
LKaemmerling authored Jun 22, 2020
1 parent 196557e commit 50a7de3
Show file tree
Hide file tree
Showing 38 changed files with 2,344 additions and 6 deletions.
4 changes: 2 additions & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ builds:
main: ./cmd/hcloud/main.go
binary: hcloud
ldflags:
- -w -X github.com/hetznercloud/cli/cli.Version=v{{.Version}}
- -w -X github.com/hetznercloud/cli/cli.Version={{.Version}}
env:
- CGO_ENABLED=0
goos:
Expand All @@ -23,7 +23,7 @@ builds:
main: ./cmd/hcloud/main.go
binary: hcloud
ldflags:
- -w -X github.com/hetznercloud/cli/cli.Version=v{{.Version}}
- -w -X github.com/hetznercloud/cli/cli.Version={{.Version}}
env:
- CGO_ENABLED=0
goos:
Expand Down
29 changes: 29 additions & 0 deletions cli/certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cli

import "github.com/spf13/cobra"

func newCertificatesCommand(cli *CLI) *cobra.Command {
cmd := &cobra.Command{
Use: "certificate",
Short: "Manage certificates",
Args: cobra.NoArgs,
TraverseChildren: true,
DisableFlagsInUseLine: true,
RunE: cli.wrap(runCertificates),
}
cmd.AddCommand(
newCertificatesListCommand(cli),
newCertificateCreateCommand(cli),
newCertificateUpdateCommand(cli),
newCertificateAddLabelCommand(cli),
newCertificateRemoveLabelCommand(cli),
newCertificateDeleteCommand(cli),
newCertificateDescribeCommand(cli),
)

return cmd
}

func runCertificates(cli *CLI, cmd *cobra.Command, args []string) error {
return cmd.Usage()
}
64 changes: 64 additions & 0 deletions cli/certificate_add_label.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cli

import (
"fmt"

"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)

func newCertificateAddLabelCommand(cli *CLI) *cobra.Command {
cmd := &cobra.Command{
Use: "add-label [FLAGS] CERTIFICATE LABEL",
Short: "Add a label to a certificate",
Args: cobra.ExactArgs(2),
TraverseChildren: true,
DisableFlagsInUseLine: true,
PreRunE: chainRunE(validateCertificateAddLabel, cli.ensureToken),
RunE: cli.wrap(runCertificateAddLabel),
}

cmd.Flags().BoolP("overwrite", "o", false, "Overwrite label if it exists already")
return cmd
}

func validateCertificateAddLabel(cmd *cobra.Command, args []string) error {
label := splitLabel(args[1])
if len(label) != 2 {
return fmt.Errorf("invalid label: %s", args[1])
}
return nil
}

func runCertificateAddLabel(cli *CLI, cmd *cobra.Command, args []string) error {
overwrite, err := cmd.Flags().GetBool("overwrite")
if err != nil {
return err
}
idOrName := args[0]
cert, _, err := cli.Client().Certificate.Get(cli.Context, idOrName)
if err != nil {
return err
}
if cert == nil {
return fmt.Errorf("Certificate not found: %s", idOrName)
}
label := splitLabel(args[1])
if _, ok := cert.Labels[label[0]]; ok && !overwrite {
return fmt.Errorf("Label %s on certificate %d already exists", label[0], cert.ID)
}
if cert.Labels == nil {
cert.Labels = make(map[string]string)
}
labels := cert.Labels
labels[label[0]] = label[1]
opts := hcloud.CertificateUpdateOpts{
Labels: labels,
}
_, _, err = cli.Client().Certificate.Update(cli.Context, cert, opts)
if err != nil {
return err
}
fmt.Printf("Label %s added to certificate %d\n", label[0], cert.ID)
return nil
}
71 changes: 71 additions & 0 deletions cli/certificate_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cli

import (
"fmt"
"io/ioutil"

"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)

func newCertificateCreateCommand(cli *CLI) *cobra.Command {
cmd := &cobra.Command{
Use: "create [FLAGS]",
Short: "Create or upload a Certificate",
Args: cobra.NoArgs,
TraverseChildren: true,
DisableFlagsInUseLine: true,
PreRunE: cli.ensureToken,
RunE: cli.wrap(runCertificateCreate),
}

cmd.Flags().String("name", "", "Certificate name")
cmd.MarkFlagRequired("name")

cmd.Flags().String("cert-file", "", "File containing the PEM encoded certificate")
cmd.MarkFlagRequired("cert-file")

cmd.Flags().String("key-file", "", "File containing the PEM encoded private key for the certificate")
cmd.MarkFlagRequired("key-file")

return cmd
}

func runCertificateCreate(cli *CLI, cmd *cobra.Command, args []string) error {
var (
name string

certFile, keyFile string
certPEM, keyPEM []byte
cert *hcloud.Certificate

err error
)
if name, err = cmd.Flags().GetString("name"); err != nil {
return err
}
if certFile, err = cmd.Flags().GetString("cert-file"); err != nil {
return err
}
if keyFile, err = cmd.Flags().GetString("key-file"); err != nil {
return err
}

if certPEM, err = ioutil.ReadFile(certFile); err != nil {
return err
}
if keyPEM, err = ioutil.ReadFile(keyFile); err != nil {
return err
}

createOpts := hcloud.CertificateCreateOpts{
Certificate: string(certPEM),
Name: name,
PrivateKey: string(keyPEM),
}
if cert, _, err = cli.Client().Certificate.Create(cli.Context, createOpts); err != nil {
return err
}
fmt.Printf("Certificate %d created\n", cert.ID)
return nil
}
36 changes: 36 additions & 0 deletions cli/certificate_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cli

import (
"fmt"

"github.com/spf13/cobra"
)

func newCertificateDeleteCommand(cli *CLI) *cobra.Command {
return &cobra.Command{
Use: "delete CERTIFICATE",
Short: "Delete a certificate",
Args: cobra.ExactArgs(1),
TraverseChildren: true,
DisableFlagsInUseLine: true,
PreRunE: cli.ensureToken,
RunE: cli.wrap(runCertificateDelete),
}
}

func runCertificateDelete(cli *CLI, cmd *cobra.Command, args []string) error {
idOrName := args[0]
cert, _, err := cli.Client().Certificate.Get(cli.Context, idOrName)
if err != nil {
return err
}
if cert == nil {
return fmt.Errorf("Certificate %s not found", idOrName)
}
_, err = cli.Client().Certificate.Delete(cli.Context, cert)
if err != nil {
return err
}
fmt.Printf("Certificate %d deleted\n", cert.ID)
return nil
}
82 changes: 82 additions & 0 deletions cli/certificate_describe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cli

import (
"encoding/json"
"fmt"

humanize "github.com/dustin/go-humanize"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)

func newCertificateDescribeCommand(cli *CLI) *cobra.Command {
cmd := &cobra.Command{
Use: "describe [FLAGS] CERTIFICATE",
Short: "Describe a certificate",
Args: cobra.ExactArgs(1),
TraverseChildren: true,
DisableFlagsInUseLine: true,
PreRunE: cli.ensureToken,
RunE: cli.wrap(runCertificateDescribe),
}
addOutputFlag(cmd, outputOptionJSON(), outputOptionFormat())
return cmd
}

func runCertificateDescribe(cli *CLI, cmd *cobra.Command, args []string) error {
outputFlags := outputFlagsForCommand(cmd)

idOrName := args[0]
cert, resp, err := cli.Client().Certificate.Get(cli.Context, idOrName)
if err != nil {
return err
}
if cert == nil {
return fmt.Errorf("certificate not found: %s", idOrName)
}

switch {
case outputFlags.IsSet("json"):
return certificateDescribeJSON(resp)
case outputFlags.IsSet("format"):
return describeFormat(cert, outputFlags["format"][0])
default:
return certificateDescribeText(cli, cert)
}
}

func certificateDescribeJSON(resp *hcloud.Response) error {
var data map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return err
}
if server, ok := data["certificate"]; ok {
return describeJSON(server)
}
if servers, ok := data["certificates"].([]interface{}); ok {
return describeJSON(servers[0])
}
return describeJSON(data)
}

func certificateDescribeText(cli *CLI, cert *hcloud.Certificate) error {
fmt.Printf("ID:\t\t\t%d\n", cert.ID)
fmt.Printf("Name:\t\t\t%s\n", cert.Name)
fmt.Printf("Fingerprint:\t\t%s\n", cert.Fingerprint)
fmt.Printf("Created:\t\t%s (%s)\n", datetime(cert.Created), humanize.Time(cert.Created))
fmt.Printf("Not valid before:\t%s (%s)\n", datetime(cert.NotValidBefore), humanize.Time(cert.NotValidBefore))
fmt.Printf("Not valid after:\t%s (%s)\n", datetime(cert.NotValidAfter), humanize.Time(cert.NotValidAfter))
fmt.Printf("Domain names:\n")
for _, domainName := range cert.DomainNames {
fmt.Printf(" - %s\n", domainName)
}
fmt.Print("Labels:\n")
if len(cert.Labels) == 0 {
fmt.Print(" No labels\n")
} else {
for key, value := range cert.Labels {
fmt.Printf(" %s:\t%s\n", key, value)
}
}
return nil
}
Loading

0 comments on commit 50a7de3

Please sign in to comment.