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

Implement CLI / Config with Cobra / Viper #1043

Merged
merged 32 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
37796a4
receptor running without cmdline tool
resoluteCoder May 8, 2024
902d630
another one
resoluteCoder May 15, 2024
cb64d8f
another one
resoluteCoder May 16, 2024
79719c3
added config version switch and defaults
resoluteCoder May 23, 2024
7ab6215
added support for env vars for v2 yml
resoluteCoder May 30, 2024
d9ad77d
added conditional for init fn for v2 config
resoluteCoder May 30, 2024
e0e89e7
another one
resoluteCoder Jun 25, 2024
5628b33
certs are a working
resoluteCoder Jun 27, 2024
39bfe48
added defaults to config the right way
resoluteCoder Jul 3, 2024
e396968
Merge branch 'devel' into ekans-arbok
resoluteCoder Jul 3, 2024
93de190
added --legacy flag to cli tests
resoluteCoder Jul 3, 2024
25a34db
linted, fixed issues
resoluteCoder Jul 5, 2024
b4eae42
Merge branch 'devel' into ekans-arbok
resoluteCoder Jul 5, 2024
9617e85
containerstuff
resoluteCoder Jul 5, 2024
b18f6dc
Merge branch 'ekans-arbok' of github.com:resoluteCoder/receptor into …
resoluteCoder Jul 5, 2024
6030c59
fixed default location
resoluteCoder Jul 8, 2024
353d768
Merge branch 'devel' into ekans-arbok
resoluteCoder Jul 8, 2024
4e95b47
added redial default and legacy to python test
resoluteCoder Jul 8, 2024
8553b93
added legacy option for python tests
resoluteCoder Jul 9, 2024
26dbb7a
fixed --help
resoluteCoder Jul 11, 2024
5d879fa
changed docs from v1 to v2
resoluteCoder Jul 11, 2024
9de74cb
removed comments
resoluteCoder Jul 11, 2024
2c94344
removed comments
resoluteCoder Jul 11, 2024
cb38a4d
Merge branch 'config-v2-docs' into ekans-arbok
resoluteCoder Jul 16, 2024
e693a16
added receptor cli usage for docs, added back sphinxext ext
resoluteCoder Jul 16, 2024
6ad3ef8
removed use of slices to support go 1.20
resoluteCoder Jul 17, 2024
50b45a6
Merge branch 'devel' into ekans-arbok
resoluteCoder Jul 17, 2024
3364a2c
updated docs for better readability, change docker container to use v…
resoluteCoder Jul 29, 2024
72139fb
changed gh wf on pr test.cfg to v2
resoluteCoder Jul 30, 2024
a6721a9
Merge branch 'devel' into ekans-arbok
resoluteCoder Jul 30, 2024
642d56a
updated container file and gh wf pr to rightfully use v2 yaml file in…
resoluteCoder Jul 30, 2024
10745fe
Merge branch 'devel' into ekans-arbok
resoluteCoder Jul 31, 2024
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
16 changes: 9 additions & 7 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,24 @@ jobs:

- name: Write out basic config
run: |
cat << EOF > test.cfg
cat << EOF > test.yaml
---
- local-only:
version: 2
local-only:
local: true

- control-service:
service: control
control-services:
- service: control
filename: /tmp/receptor.sock

- work-command:
worktype: cat
work-commands:
- worktype: cat
command: cat
EOF

- name: Run receptor (and wait a few seconds for it to boot)
run: |
podman run --name receptor -d -v $PWD/test.cfg:/etc/receptor/receptor.conf:Z localhost/receptor
podman run --name receptor -d -v $PWD/test.yaml:/etc/receptor/receptor.yaml:Z localhost/receptor
sleep 3
podman logs receptor

Expand Down
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ linters-settings:
allow:
- "$gostd"
- "github.com/ansible/receptor/internal/version"
- "github.com/ansible/receptor/cmd"
- "github.com/ansible/receptor/pkg"
- "github.com/creack/pty"
- "github.com/fsnotify/fsnotify"
Expand All @@ -70,6 +71,8 @@ linters-settings:
- "github.com/rogpeppe/go-internal/lockedfile"
- "github.com/songgao/water"
- "github.com/vishvananda/netlink"
- "github.com/spf13/viper"
- "github.com/spf13/cobra"
- "k8s.io/api/core"
- "k8s.io/apimachinery/pkg"
- "k8s.io/client-go"
Expand Down
4 changes: 0 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,6 @@ build-all:
./cmd/receptor-cl && \
$(GO) build \
example/*.go && \
$(GO) build \
-o receptor \
--tags no_backends,no_services,no_tls_config \
./cmd/receptor-cl && \
$(GO) build \
-o receptor \
-ldflags "-X 'github.com/ansible/receptor/internal/version.Version=$(VERSION)'" \
Expand Down
217 changes: 217 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package cmd

import (
"fmt"
"os"
"reflect"

"github.com/ansible/receptor/pkg/backends"
"github.com/ansible/receptor/pkg/certificates"
"github.com/ansible/receptor/pkg/controlsvc"
"github.com/ansible/receptor/pkg/logger"
"github.com/ansible/receptor/pkg/netceptor"
"github.com/ansible/receptor/pkg/services"
"github.com/ansible/receptor/pkg/types"
"github.com/ansible/receptor/pkg/workceptor"
"github.com/ghjm/cmdline"
"github.com/spf13/viper"
)

type Initer interface {
Init() error
}

type Preparer interface {
Prepare() error
}

type Runer interface {
Run() error
}

type ReceptorConfig struct {
// Used pointer structs to apply defaults to config
Node *types.NodeCfg
Trace logger.TraceCfg
LocalOnly backends.NullBackendCfg `mapstructure:"local-only"`
LogLevel *logger.LoglevelCfg `mapstructure:"log-level"`
ControlServices []*controlsvc.CmdlineConfigUnix `mapstructure:"control-services"`
TCPPeers []*backends.TCPDialerCfg `mapstructure:"tcp-peers"`
UDPPeers []*backends.UDPDialerCfg `mapstructure:"udp-peers"`
WSPeers []*backends.WebsocketDialerCfg `mapstructure:"ws-peers"`
TCPListeners []*backends.TCPListenerCfg `mapstructure:"tcp-listeners"`
UDPListeners []*backends.UDPListenerCfg `mapstructure:"udp-listeners"`
WSListeners []*backends.WebsocketListenerCfg `mapstructure:"ws-listeners"`
TLSClients []netceptor.TLSClientConfig `mapstructure:"tls-clients"`
TLSServer []netceptor.TLSServerConfig `mapstructure:"tls-servers"`
WorkCommands []workceptor.CommandWorkerCfg `mapstructure:"work-commands"`
WorkKubernetes []*workceptor.KubeWorkerCfg `mapstructure:"work-kubernetes"`
WorkSigning workceptor.SigningKeyPrivateCfg `mapstructure:"work-signing"`
WorkVerification workceptor.VerifyingKeyPublicCfg `mapstructure:"work-verification"`
IPRouters []services.IPRouterCfg
TCPClients []services.TCPProxyOutboundCfg `mapstructure:"tcp-clients"`
TCPServers []services.TCPProxyInboundCfg `mapstructure:"tcp-servers"`
UDPClients []services.TCPProxyInboundCfg `mapstructure:"udp-clients"`
UDPServers []services.TCPProxyInboundCfg `mapstructure:"udp-servers"`
UnixSocketClients []services.UnixProxyOutboundCfg `mapstructure:"unix-socket-clients"`
UnixSocketServers []services.UnixProxyInboundCfg `mapstructure:"unix-socket-servers"`
}

type CertificatesConfig struct {
InitCA certificates.InitCAConfig `mapstructure:"cert-init"`
MakeReq []certificates.MakeReqConfig `mapstructure:"cert-makereqs"`
SignReq []certificates.SignReqConfig `mapstructure:"cert-signreqs"`
}

func PrintPhaseErrorMessage(configName string, phase string, err error) {
fmt.Printf("ERROR: %s for %s on %s phase\n", err, configName, phase)
}

func ParseConfigs(configFile string) (*ReceptorConfig, *CertificatesConfig, error) {
if configFile == "" && viper.ConfigFileUsed() == "" {
fmt.Fprintln(os.Stderr, "Could not locate config file (default is $HOME/receptor.yaml)")
os.Exit(1)
}
var receptorConfig ReceptorConfig
var certifcatesConfig CertificatesConfig
err := viper.Unmarshal(&receptorConfig)
if err != nil {
return nil, nil, err
}

err = viper.Unmarshal(&certifcatesConfig)
if err != nil {
return nil, nil, err
}

return &receptorConfig, &certifcatesConfig, nil
}

func isConfigEmpty(v reflect.Value) bool {
isEmpty := true
for i := 0; i < v.NumField(); i++ {
if reflect.Value.IsZero(v.Field(i)) {
continue
}
isEmpty = false
}

return isEmpty
}

func RunConfigV2(v reflect.Value) {
phases := []string{"Init", "Prepare", "Run"}

for _, phase := range phases {
for i := 0; i < v.NumField(); i++ {
if reflect.Value.IsZero(v.Field(i)) {
continue
}

switch v.Field(i).Kind() {
case reflect.Slice:
for j := 0; j < v.Field(i).Len(); j++ {
RunPhases(phase, v.Field(i).Index(j))
}
default:
RunPhases(phase, v.Field(i))
}
}
}
}

// RunPhases runs the appropriate function (Init, Prepare, Run) on a command.
func RunPhases(phase string, v reflect.Value) {
resoluteCoder marked this conversation as resolved.
Show resolved Hide resolved
cmd := v.Interface()
var err error

if phase == "Init" {
switch c := cmd.(type) {
case Initer:
err = c.Init()
if err != nil {
PrintPhaseErrorMessage(v.Type().Name(), phase, err)
}
default:
}
}
if phase == "Prepare" {
switch c := cmd.(type) {
case Preparer:
err = c.Prepare()
if err != nil {
PrintPhaseErrorMessage(v.Type().Name(), phase, err)
}
default:
}
}
if phase == "Run" {
switch c := cmd.(type) {
case Runer:
err = c.Run()
if err != nil {
PrintPhaseErrorMessage(v.Type().Name(), phase, err)
}
default:
}
}
}

func RunConfigV1() {
cl := cmdline.NewCmdline()
cl.AddConfigType("node", "Specifies the node configuration of this instance", types.NodeCfg{}, cmdline.Required, cmdline.Singleton)
cl.AddConfigType("local-only", "Runs a self-contained node with no backend", backends.NullBackendCfg{}, cmdline.Singleton)

// Add registered config types from imported modules
for _, appName := range []string{
"receptor-version",
"receptor-logging",
"receptor-tls",
"receptor-certificates",
"receptor-control-service",
"receptor-command-service",
"receptor-ip-router",
"receptor-proxies",
"receptor-backends",
"receptor-workers",
} {
cl.AddRegisteredConfigTypes(appName)
}

osArgs := os.Args[1:]

err := cl.ParseAndRun(osArgs, []string{"Init", "Prepare", "Run"}, cmdline.ShowHelpIfNoArgs)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
if cl.WhatRan() != "" {
// We ran an exclusive command, so we aren't starting any back-ends
os.Exit(0)
}

configPath := ""
for i, arg := range osArgs {
if arg == "--config" || arg == "-c" {
if len(osArgs) > i+1 {
configPath = osArgs[i+1]
}

break
}
}

// only allow reloading if a configuration file was provided. If ReloadCL is
// not set, then the control service reload command will fail
if configPath != "" {
// create closure with the passed in args to be ran during a reload
reloadParseAndRun := func(toRun []string) error {
return cl.ParseAndRun(osArgs, toRun)
}
err = controlsvc.InitReload(configPath, reloadParseAndRun)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
}
}
Loading