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

Rufio options #6741

Merged
merged 15 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 11 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
66 changes: 66 additions & 0 deletions cmd/eksctl-anywhere/cmd/aflag/aflag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Package aflag is the eks anywhere flag handling package.
package aflag

import (
"github.com/spf13/pflag"
)

// Flag defines a CLI flag.
type Flag[T any] struct {
Name string
Short string
Usage string
Default T
}

// String applies f to fs and writes the value to dst.
func String(f Flag[string], dst *string, fs *pflag.FlagSet) {
switch {

Check warning on line 18 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L17-L18

Added lines #L17 - L18 were not covered by tests
// With short form
case f.Short != "":
fs.StringVarP(dst, f.Name, f.Short, f.Default, f.Usage)

Check warning on line 21 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L20-L21

Added lines #L20 - L21 were not covered by tests
// Without short form
default:
fs.StringVar(dst, f.Name, f.Default, f.Usage)

Check warning on line 24 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L23-L24

Added lines #L23 - L24 were not covered by tests
}
}

// Bool applies f to fs and writes the value to dst.
func Bool(f Flag[bool], dst *bool, fs *pflag.FlagSet) {
switch {
case f.Short != "":
fs.BoolVarP(dst, f.Name, f.Short, f.Default, f.Usage)
default:
fs.BoolVar(dst, f.Name, f.Default, f.Usage)

Check warning on line 34 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L29-L34

Added lines #L29 - L34 were not covered by tests
}
}

// StringSlice applies f to fs and writes the value to dst.
func StringSlice(f Flag[[]string], dst *[]string, fs *pflag.FlagSet) {
switch {
case f.Short != "":
fs.StringSliceVarP(dst, f.Name, f.Short, f.Default, f.Usage)
default:
fs.StringSliceVar(dst, f.Name, f.Default, f.Usage)

Check warning on line 44 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L39-L44

Added lines #L39 - L44 were not covered by tests
}
}

// StringString applies f to fs and writes the value to dst.
func StringString(f Flag[map[string]string], dst *map[string]string, fs *pflag.FlagSet) {
switch {
case f.Short != "":
fs.StringToStringVarP(dst, f.Name, f.Short, f.Default, f.Usage)
default:
fs.StringToStringVar(dst, f.Name, f.Default, f.Usage)

Check warning on line 54 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L49-L54

Added lines #L49 - L54 were not covered by tests
}
}

// HTTPHeader applies f to fs and writes the value to dst.
func HTTPHeader(f Flag[Header], dst *Header, fs *pflag.FlagSet) {
switch {
case f.Short != "":
fs.VarP(dst, f.Name, f.Short, f.Usage)
default:
fs.Var(dst, f.Name, f.Usage)

Check warning on line 64 in cmd/eksctl-anywhere/cmd/aflag/aflag.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/aflag.go#L59-L64

Added lines #L59 - L64 were not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package flags
package aflag

// ClusterConfig is the path to a cluster specification YAML.
var ClusterConfig = Flag[string]{
Expand Down
63 changes: 63 additions & 0 deletions cmd/eksctl-anywhere/cmd/aflag/header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package aflag

import (
"encoding/csv"
"encoding/json"
"fmt"
"net/http"
"strings"
)

// Header Value.
type Header http.Header

// NewHeader returns a new Header pointer.
func NewHeader(h *http.Header) *Header {
return (*Header)(h)

Check warning on line 16 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L15-L16

Added lines #L15 - L16 were not covered by tests
}

// String returns the string representation of the Header.
func (h *Header) String() string {
if b, err := json.Marshal(h); err == nil {
return string(b)
}

Check warning on line 23 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L20-L23

Added lines #L20 - L23 were not covered by tests

return ""

Check warning on line 25 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L25

Added line #L25 was not covered by tests
}

// Set sets the value of the Header.
// Format: "a=1;2,b=2;4;5".
func (h *Header) Set(val string) error {
var ss []string
n := strings.Count(val, "=")
switch n {
case 0:
return fmt.Errorf("%s must be formatted as key=value;value", val)
case 1:
ss = append(ss, strings.Trim(val, `"`))
default:
r := csv.NewReader(strings.NewReader(val))
var err error
ss, err = r.Read()
if err != nil {
return err
}

Check warning on line 44 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L30-L44

Added lines #L30 - L44 were not covered by tests
}

out := make(map[string][]string, len(ss))
for _, pair := range ss {
kv := strings.SplitN(pair, "=", 2)
if len(kv) != 2 {
return fmt.Errorf("%s must be formatted as key=value;value", pair)
}
out[kv[0]] = strings.Split(kv[1], ";")

Check warning on line 53 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L47-L53

Added lines #L47 - L53 were not covered by tests
}
*h = out

return nil

Check warning on line 57 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L55-L57

Added lines #L55 - L57 were not covered by tests
}

// Type returns the flag type.
func (h *Header) Type() string {
return "header"

Check warning on line 62 in cmd/eksctl-anywhere/cmd/aflag/header.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/header.go#L61-L62

Added lines #L61 - L62 were not covered by tests
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package flags
package aflag

import (
"github.com/spf13/cobra"
Expand All @@ -13,3 +13,12 @@
}
}
}

// MarkHidden is a helper to mark flags hidden on cmd. If a flag does not exist, it panics.
func MarkHidden(set *pflag.FlagSet, flags ...string) {
for _, flag := range flags {
if err := set.MarkHidden(flag); err != nil {
panic(err)

Check warning on line 21 in cmd/eksctl-anywhere/cmd/aflag/marker.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/aflag/marker.go#L18-L21

Added lines #L18 - L21 were not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package flags_test
package aflag_test

import (
"testing"

"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags"
"github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag"
)

func TestMarkRequired(t *testing.T) {
Expand Down Expand Up @@ -77,7 +77,7 @@ func TestMarkRequired(t *testing.T) {
// so we need to parse the args using the flag set before calling it.
_ = cmd.Flags().Parse(tc.Args)

flags.MarkRequired(cmd.Flags(), required...)
aflag.MarkRequired(cmd.Flags(), required...)

err := cmd.ValidateRequiredFlags()

Expand Down Expand Up @@ -109,7 +109,7 @@ func TestMarkRequired_FlagDoesNotExist(t *testing.T) {
}()

flgs := pflag.NewFlagSet("", pflag.ContinueOnError)
flags.MarkRequired(flgs, "does-not-exist")
aflag.MarkRequired(flgs, "does-not-exist")
}

func nopPflag(name string) pflag.Flag {
Expand Down
101 changes: 101 additions & 0 deletions cmd/eksctl-anywhere/cmd/aflag/tinkerbell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package aflag

// TinkerbellBootstrapIP is used to override the Tinkerbell IP for serving a Tinkerbell stack
// from an admin machine.
var TinkerbellBootstrapIP = Flag[string]{
Name: "tinkerbell-bootstrap-ip",
Usage: "The IP used to expose the Tinkerbell stack from the bootstrap cluster",
}

// TinkerbellBMCConsumerURL is a Rufio RPC provider option.
// ConsumerURL is the URL where an rpc consumer/listener is running and to which we will send and receive all notifications.
var TinkerbellBMCConsumerURL = Flag[string]{
Name: "tinkerbell-bmc-consumer-url",
Usage: "The URL of a BMC RPC consumer/listener used for BMC interactions",
}

// TinkerbellBMCHTTPContentType is a Rufio RPC provider option.
// The content type header to use for the rpc request notification.
var TinkerbellBMCHTTPContentType = Flag[string]{
Name: "tinkerbell-bmc-http-content-type",
Usage: "The HTTP content type used for the RPC BMC interactions",
}

// TinkerbellBMCHTTPMethod is a Rufio RPC provider option.
// The HTTP method to use for the rpc request notification.
var TinkerbellBMCHTTPMethod = Flag[string]{
Name: "tinkerbell-bmc-http-method",
Usage: "The HTTP method used for the RPC BMC interactions",
}

// TinkerbellBMCTimestampHeader is a Rufio RPC provider option.
// The the header name that should contain the timestamp.
// Example: X-BMCLIB-Timestamp (in RFC3339 format)
// .
var TinkerbellBMCTimestampHeader = Flag[string]{
Name: "tinkerbell-bmc-timestamp-header",
Usage: "The HTTP timestamp header used for the RPC BMC interactions",
}

// TinkerbellBMCStaticHeaders is a Rufio RPC provider option.
// Predefined headers that will be added to every request (comma separated, values are semicolon separated)
// Example: "X-My-Header=1;2;3,X-Custom-Header=abc;def"
// .
var TinkerbellBMCStaticHeaders = Flag[Header]{
Name: "tinkerbell-bmc-static-headers",
Usage: "Static HTTP headers added to all RPC BMC interactions",
}

// TinkerbellBMCSigHeaderName is a Rufio RPC provider option.
// The header name that should contain the signature(s).
// Example: X-BMCLIB-Signature
// .
var TinkerbellBMCSigHeaderName = Flag[string]{
Name: "tinkerbell-bmc-sig-header-name",
Usage: "The HTTP header name for the HMAC signature used in RPC BMC interactions",
}

// TinkerbellBMCAppendAlgoToHeaderDisabled is a Rufio RPC provider option.
// decides whether to append the algorithm to the signature header or not.
// Example: X-BMCLIB-Signature becomes X-BMCLIB-Signature-256
// .
var TinkerbellBMCAppendAlgoToHeaderDisabled = Flag[bool]{
Name: "tinkerbell-bmc-append-algo-to-header-disabled",
Usage: "This disables appending of the algorithm type to the signature header used in RPC BMC interactions",
}

// TinkerbellBMCSigIncludedPayloadHeaders is a Rufio RPC provider option.
// The headers whose values will be included in the signature payload.
// Example: given these headers in a request: X-My-Header=123,X-Another=456,
// and IncludedPayloadHeaders := []string{"X-Another"}, the value of "X-Another"
// will be included in the signature payload (comma separated).
var TinkerbellBMCSigIncludedPayloadHeaders = Flag[[]string]{
Name: "tinkerbell-bmc-sig-included-payload-headers",
Usage: "The HTTP headers to be included in the HMAC signature payload used in RPC BMC interactions",
}

// TinkerbellBMCPrefixSigDisabled is a Rufio RPC provider option.
// Example: sha256=abc123 ; Determines whether the algorithm will be prefixed to the signature.
var TinkerbellBMCPrefixSigDisabled = Flag[bool]{
Name: "tinkerbell-bmc-prefix-sig-disabled",
Usage: "This disables prefixing the signature with the algorithm type used in RPC BMC interactions",
}

// TinkerbellBMCHMACSecrets is a Rufio RPC provider option.
// secrets used for signing the payload, all secrets with used to sign with both sha256 and sha512.
var TinkerbellBMCHMACSecrets = Flag[[]string]{
Name: "tinkerbell-bmc-hmac-secrets",
Usage: "The secrets used to HMAC sign a payload, used in RPC BMC interactions",
}

// TinkerbellBMCCustomPayload allows providing a JSON payload that will be used in the RPC request.
var TinkerbellBMCCustomPayload = Flag[string]{
Name: "tinkerbell-bmc-custom-payload",
Usage: "The custom payload used in RPC BMC interactions, must be used with tinkerbell-bmc-custom-payload-dot-location",
}

// TinkerbellBMCCustomPayloadDotLocation is the path to where the bmclib RequestPayload{} will be embedded. For example: object.data.body.
var TinkerbellBMCCustomPayloadDotLocation = Flag[string]{
Name: "tinkerbell-bmc-custom-payload-dot-location",
Usage: "The dot location of the custom payload used in RPC BMC interactions, must be used with tinkerbell-bmc-custom-payload",
}
49 changes: 44 additions & 5 deletions cmd/eksctl-anywhere/cmd/createcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"strings"

"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags"
"github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag"
"github.com/aws/eks-anywhere/pkg/api/v1alpha1"
"github.com/aws/eks-anywhere/pkg/awsiamauth"
"github.com/aws/eks-anywhere/pkg/clustermanager"
Expand All @@ -16,6 +17,7 @@
"github.com/aws/eks-anywhere/pkg/features"
"github.com/aws/eks-anywhere/pkg/kubeconfig"
"github.com/aws/eks-anywhere/pkg/logger"
"github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware"
"github.com/aws/eks-anywhere/pkg/types"
"github.com/aws/eks-anywhere/pkg/validations"
"github.com/aws/eks-anywhere/pkg/validations/createvalidations"
Expand All @@ -32,9 +34,18 @@
tinkerbellBootstrapIP string
installPackages string
skipValidations []string
providerOptions *dependencies.ProviderOptions
}

var cc = &createClusterOptions{}
var cc = &createClusterOptions{
providerOptions: &dependencies.ProviderOptions{
Tinkerbell: &dependencies.TinkerbellOptions{
BMCOptions: &hardware.BMCOptions{
RPC: &hardware.RPCOpts{},
},
},
},
}

var createClusterCmd = &cobra.Command{
Use: "cluster -f <cluster-config-file> [flags]",
Expand All @@ -50,14 +61,42 @@
applyClusterOptionFlags(createClusterCmd.Flags(), &cc.clusterOptions)
applyTimeoutFlags(createClusterCmd.Flags(), &cc.timeoutOptions)
applyTinkerbellHardwareFlag(createClusterCmd.Flags(), &cc.hardwareCSVPath)
flags.String(flags.TinkerbellBootstrapIP, &cc.tinkerbellBootstrapIP, createClusterCmd.Flags())
aflag.String(aflag.TinkerbellBootstrapIP, &cc.tinkerbellBootstrapIP, createClusterCmd.Flags())
createClusterCmd.Flags().BoolVar(&cc.forceClean, "force-cleanup", false, "Force deletion of previously created bootstrap cluster")
hideForceCleanup(createClusterCmd.Flags())
createClusterCmd.Flags().BoolVar(&cc.skipIpCheck, "skip-ip-check", false, "Skip check for whether cluster control plane ip is in use")
createClusterCmd.Flags().StringVar(&cc.installPackages, "install-packages", "", "Location of curated packages configuration files to install to the cluster")
createClusterCmd.Flags().StringArrayVar(&cc.skipValidations, "skip-validations", []string{}, fmt.Sprintf("Bypass create validations by name. Valid arguments you can pass are --skip-validations=%s", strings.Join(createvalidations.SkippableValidations[:], ",")))
tinkerbellFlags(createClusterCmd.Flags(), cc.providerOptions.Tinkerbell.BMCOptions.RPC)

aflag.MarkRequired(createClusterCmd.Flags(), aflag.ClusterConfig.Name)
}

flags.MarkRequired(createClusterCmd.Flags(), flags.ClusterConfig.Name)
func tinkerbellFlags(fs *pflag.FlagSet, r *hardware.RPCOpts) {
aflag.String(aflag.TinkerbellBMCConsumerURL, &r.ConsumerURL, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCConsumerURL.Name)
Copy link
Contributor

@chrisdoherty4 chrisdoherty4 Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for hiding these?

I am OK with the env/cli variables. Curious if we thought about including these in the cluster config somehow? Historically there's been a strong preference for using the cluster config and this seems like something that could feature on the datacenter config?

@vivek-koppuru @jiayiwang7 @abhinavmpandey08

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for hiding these? These are what i would consider advanced options and not something I would recommend we clutter a cli help message with.

Curious if we thought about including these in the cluster config somehow? Yeah, i did consider it. The concern i have for adding to the cluster config is that i believe we don't understand enough about how/if/when customers will use this functionality. I would prefer to hide it like i did and learn more before adding it to the cluster config where deprecating feels a lot more difficult.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great points, agreed.

aflag.String(aflag.TinkerbellBMCHTTPContentType, &r.Request.HTTPContentType, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCHTTPContentType.Name)
aflag.String(aflag.TinkerbellBMCHTTPMethod, &r.Request.HTTPMethod, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCHTTPMethod.Name)
aflag.String(aflag.TinkerbellBMCTimestampHeader, &r.Request.TimestampHeader, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCTimestampHeader.Name)
aflag.HTTPHeader(aflag.TinkerbellBMCStaticHeaders, aflag.NewHeader(&r.Request.StaticHeaders), fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCStaticHeaders.Name)
aflag.String(aflag.TinkerbellBMCSigHeaderName, &r.Signature.HeaderName, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCSigHeaderName.Name)
aflag.Bool(aflag.TinkerbellBMCAppendAlgoToHeaderDisabled, &r.Signature.AppendAlgoToHeaderDisabled, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCAppendAlgoToHeaderDisabled.Name)
aflag.StringSlice(aflag.TinkerbellBMCSigIncludedPayloadHeaders, &r.Signature.IncludedPayloadHeaders, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCSigIncludedPayloadHeaders.Name)
aflag.Bool(aflag.TinkerbellBMCPrefixSigDisabled, &r.HMAC.PrefixSigDisabled, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCPrefixSigDisabled.Name)
aflag.StringSlice(aflag.TinkerbellBMCHMACSecrets, &r.HMAC.Secrets, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCHMACSecrets.Name)
aflag.String(aflag.TinkerbellBMCCustomPayload, &r.Experimental.CustomRequestPayload, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCCustomPayload.Name)
aflag.String(aflag.TinkerbellBMCCustomPayloadDotLocation, &r.Experimental.DotPath, fs)
aflag.MarkHidden(fs, aflag.TinkerbellBMCCustomPayloadDotLocation.Name)
}

func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) error {
Expand Down Expand Up @@ -141,7 +180,7 @@
WithBootstrapper().
WithCliConfig(cliConfig).
WithClusterManager(clusterSpec.Cluster, clusterManagerTimeoutOpts).
WithProvider(cc.fileName, clusterSpec.Cluster, cc.skipIpCheck, cc.hardwareCSVPath, cc.forceClean, cc.tinkerbellBootstrapIP, skippedValidations).
WithProvider(cc.fileName, clusterSpec.Cluster, cc.skipIpCheck, cc.hardwareCSVPath, cc.forceClean, cc.tinkerbellBootstrapIP, skippedValidations, cc.providerOptions).

Check warning on line 183 in cmd/eksctl-anywhere/cmd/createcluster.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/createcluster.go#L183

Added line #L183 was not covered by tests
WithGitOpsFlux(clusterSpec.Cluster, clusterSpec.FluxConfig, cliConfig).
WithWriter().
WithEksdInstaller().
Expand Down
Loading