Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): create first draft #26

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

[*.go]
indent_size = 2
indent_style = tab

[Makefile]
indent_style = tab
indent_size = 4
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ VERSION ?= $(shell git describe --tags --always --dirty)
# to build. The current options are: operator, oidc-proxy
COMPONENT ?= operator

# SOURCES define the source files used to build the project.
SOURCES = $(shell find . -type f -name '*.go')

# CHANNELS define the bundle channels used in the bundle.
# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
# To re-generate a bundle for other specific channels without changing the standard setup, you can:
Expand Down Expand Up @@ -82,12 +85,14 @@ CONTAINER_TOOL ?= docker
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec



.PHONY: all
all: build

# TODO: Add UPX compression to the build process.
bin/%: cmd/%/*.go
CGO_ENABLED=0 go build $(GOFLAGS) -o $@ $<
bin/$(COMPONENT): $(SOURCES)
CGO_ENABLED=0 go build $(GOFLAGS) -o $@ cmd/$(COMPONENT)/*.go

##@ General

Expand Down Expand Up @@ -132,7 +137,7 @@ test: manifests generate fmt vet envtest ## Run tests.
##@ Build

.PHONY: build
build: manifests generate fmt vet $(subst cmd,bin,$(shell find cmd/* -type d)) ## Build manager binary.
build: manifests generate fmt vet $(subst cmd,bin,$(shell find cmd/* -maxdepth 0 -type d)) ## Build manager binary.

.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
Expand Down
56 changes: 56 additions & 0 deletions cmd/krt/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"os"

"github.com/spf13/cobra"

"github.com/nicklasfrahm/kraut/pkg/log"
)

var version = "dev"
var help bool
var logger = log.NewSingletonLogger(log.WithCLI())

var rootCmd = &cobra.Command{
Use: "krt",
Short: "A CLI to manage infrastructure",
Long: ` _ _
| | ___ __| |_
| |/ / '__| __|
| <| | | |_
|_|\_\_| \__|

krt is a CLI to manage infrastructure. It provides
a variety of commands to manage different stages
of the infrastructure lifecycle.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if help {
cmd.Help()
os.Exit(0)
}
},
RunE: func(cmd *cobra.Command, args []string) error {
cmd.Help()
os.Exit(1)
return nil
},
Version: version,
SilenceUsage: true,
SilenceErrors: true,
}

func init() {
rootCmd.PersistentFlags().BoolVarP(&help, "help", "h", false, "Print this help")
// TODO: Allow JSON logging.
// rootCmd.PersistentFlags().BoolVar(&help, "log-json", false, "Print logs in JSON format")

rootCmd.AddCommand(zoneCommand)
rootCmd.AddCommand(sshCommand)
}

func main() {
if err := rootCmd.Execute(); err != nil {
logger.Fatal(err.Error())
}
}
23 changes: 23 additions & 0 deletions cmd/krt/ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"os"

"github.com/spf13/cobra"
)

var sshCommand = &cobra.Command{
Use: "ssh",
Short: `Perform SSH operations`,
Long: `This command group allows it to perform operations
via SSH on a server or a group of servers.`,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.Help()
os.Exit(1)
return nil
},
}

func init() {
sshCommand.AddCommand(sshFingerprintCmd)
}
64 changes: 64 additions & 0 deletions cmd/krt/ssh_fingerprint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"fmt"
"os"
"sync"
"text/tabwriter"

_ "github.com/joho/godotenv/autoload"
"github.com/spf13/cobra"

"github.com/nicklasfrahm/kraut/pkg/sshx"
)

var sshFingerprintCmd = &cobra.Command{
Use: "fingerprint <host> [...hosts]",
Short: "Fetches the SSH fingerprint of a host",
Long: `This command will fetch the SSH fingerprint of
the specified host and print it to the console.

The format is a SHA256 fingerprint of the host's
public key, similar to "SHA256:<hash>".`,
Args: cobra.MinimumNArgs(1),
ArgAliases: []string{"host"},
ValidArgs: []string{"host"},
RunE: func(cmd *cobra.Command, args []string) error {
// This should be safe because of the MinimumNArgs(1) constraint,
// but we still need to check it to avoid panics.
if len(args) < 1 {
logger.Fatal("expected at lease 1 argument")
}
hosts := args

// TODO: Move this into a function called
// `ProbeSSHHostPublicKeyFingerprints` in
// the sshx pacakge.
var wg sync.WaitGroup
fingerprints := make([]string, len(hosts))
for i, host := range hosts {
wg.Add(1)
go func(i int, host string) {
defer wg.Done()

fingerprint, err := sshx.ProbeSSHHostPublicKeyFingerprint(host)
if err != nil {
fingerprint = err.Error()
}

fingerprints[i] = fingerprint
}(i, host)
}
wg.Wait()

tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(tw, "HOST\tFINGERPRINT")
for i, host := range hosts {
fingerprint := fingerprints[i]
fmt.Fprintf(tw, "%s\t%s\n", host, fingerprint)
}
tw.Flush()

return nil
},
}
23 changes: 23 additions & 0 deletions cmd/krt/zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"os"

"github.com/spf13/cobra"
)

var zoneCommand = &cobra.Command{
Use: "zone",
Short: `Manage availability zones`,
Long: `Bootstrap new availability zones and manage the
lifecycle of existing ones.`,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.Help()
os.Exit(1)
return nil
},
}

func init() {
zoneCommand.AddCommand(zoneUpCmd)
}
62 changes: 62 additions & 0 deletions cmd/krt/zone_up.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"net"

_ "github.com/joho/godotenv/autoload"
"github.com/spf13/cobra"
"go.uber.org/zap"

"github.com/nicklasfrahm/kraut/pkg/zone"
)

var (
zoneUpCmdConfig = zone.Zone{
Router: &zone.ZoneRouter{},
}
configFile string
)

var zoneUpCmd = &cobra.Command{
Use: "up <host>",
Short: "Bootstrap a new availability zone",
Long: `This command will bootstrap a new zone by connecting
to the specified IP and setting up a k3s cluster on
the host that will then set up the required services
for managing the lifecycle of the zone.

To manage a zone, the CLI needs credentials for the
DNS provider that is used to manage the DNS records
for the zone. These credentials can only be provided
via the environment variable DNS_PROVIDER_CREDENTIAL
and DNS_PROVIDER or via a ".env" file in the current
working directory.`,
Args: cobra.ExactArgs(1),
ArgAliases: []string{"host"},
ValidArgs: []string{"host"},
RunE: func(cmd *cobra.Command, args []string) error {
// This should be safe because of the ExactArgs(1) constraint,
// but we still need to check it to avoid panics.
if len(args) != 1 {
logger.Fatal("expected exactly one argument", zap.Strings("args", args))
}
host := args[0]

if err := zone.Up(host, &zoneUpCmdConfig); err != nil {
return err
}

return nil
},
}

func init() {
_, defaultSubnet, _ := net.ParseCIDR("0.0.0.0/0")

zoneUpCmd.Flags().StringVarP(&zoneUpCmdConfig.Name, "name", "n", "", "name of the zone")
zoneUpCmd.Flags().StringVarP(&zoneUpCmdConfig.Domain, "domain", "d", "", "domain that will contain the DNS records for the zone")
zoneUpCmd.Flags().StringVarP(&zoneUpCmdConfig.Router.Hostname, "hostname", "H", "", "hostname of the router serving the zone")
zoneUpCmd.Flags().IPNetVarP(&zoneUpCmdConfig.Router.GatewaySubnet, "gateway-subnet", "g", *defaultSubnet, "IPv4 address of the router serving the zone")
zoneUpCmd.Flags().Uint32VarP(&zoneUpCmdConfig.Router.ASN, "asn", "a", 0, "autonomous system number of the zone")
zoneUpCmd.Flags().StringVarP(&configFile, "config", "c", "", "path to the configuration file")
}
12 changes: 2 additions & 10 deletions cmd/oidc-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"time"

"github.com/labstack/echo/v4"
"github.com/nicklasfrahm/kraut/pkg/log"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -50,15 +50,7 @@ func main() {
return
}

config := zap.NewProductionConfig()
if version == "dev" {
config = zap.NewDevelopmentConfig()
}

config.OutputPaths = []string{"stdout"}
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := config.Build()
logger := log.NewLogger()

clientSet, err := createKubernetesClientset()
if err != nil {
Expand Down
Loading
Loading