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

Backport of https://github.com/hashicorp/boundary/pull/5027 onto release/0.16.x #5060

Merged
merged 6 commits into from
Aug 30, 2024
Merged
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
7 changes: 7 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
"math"
"net"
"net/http"
"net/url"
Expand Down Expand Up @@ -308,6 +309,12 @@ func (c *Config) ReadEnvironment() error {
if err != nil {
return err
}
// maxRetries is a 32-bit unsigned integer stored inside an uint64.
// c.MaxRetries is a signed integer that is at least 32 bits in size.
// Check bounds against lowest denominator before casting.
if maxRetries > math.MaxInt32 {
return fmt.Errorf("max retries must be less than or equal to %d", math.MaxInt32)
}
c.MaxRetries = int(maxRetries)
}

Expand Down
72 changes: 72 additions & 0 deletions api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
package api

import (
"math"
"os"
"strconv"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestConfigSetAddress(t *testing.T) {
Expand Down Expand Up @@ -79,3 +83,71 @@ func TestConfigSetAddress(t *testing.T) {
})
}
}

func TestReadEnvironmentMaxRetries(t *testing.T) {
tests := []struct {
name string
inp string
expMaxRetries int
expErrContains string
}{
{
name: "invalidNaN",
inp: "bad",
expErrContains: "strconv.ParseUint: parsing \"bad\": invalid syntax",
},
{
name: "invalidNegativeNumber",
inp: "-1",
expErrContains: "strconv.ParseUint: parsing \"-1\": invalid syntax",
},
{
name: "invalidGreaterThanUint32",
inp: strconv.Itoa(math.MaxUint32 + 10),
expErrContains: "strconv.ParseUint: parsing \"4294967305\": value out of range",
},
{
name: "invalidGreaterThanInt32",
inp: strconv.Itoa(math.MaxInt32 + 10),
expErrContains: "max retries must be less than or equal to 2147483647",
},
{
name: "success1",
inp: "0",
expMaxRetries: 0,
},
{
name: "success2",
inp: "10000",
expMaxRetries: 10000,
},
{
name: "successMaxInt32",
inp: strconv.Itoa(math.MaxInt32),
expMaxRetries: math.MaxInt32,
},
{
name: "successNothing",
inp: "",
expMaxRetries: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Setenv(EnvBoundaryMaxRetries, tt.inp)
t.Cleanup(func() { os.Unsetenv(EnvBoundaryMaxRetries) })

var c Config
err := c.ReadEnvironment()
if tt.expErrContains != "" {
require.ErrorContains(t, err, tt.expErrContains)
require.Equal(t, 0, c.MaxRetries)
return
}

require.NoError(t, err)
require.Equal(t, tt.expMaxRetries, c.MaxRetries)
})
}
}
6 changes: 3 additions & 3 deletions internal/cmd/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ type Command struct {
FlagSkipCacheDaemon bool
FlagSkipClientAgent bool

FlagClientAgentPort uint
FlagClientAgentPort uint16

FlagScopeId string
FlagScopeName string
Expand All @@ -128,7 +128,7 @@ type Command struct {
FlagAuthMethodId string
FlagHostCatalogId string
FlagCredentialStoreId string
FlagVersion int
FlagVersion int64
FlagRecursive bool
FlagFilter string
FlagTags map[string][]string
Expand Down Expand Up @@ -500,7 +500,7 @@ func (c *Command) FlagSet(bit FlagSetBit) *FlagSets {
Hidden: true,
})

f.UintVar(&UintVar{
f.Uint16Var(&Uint16Var{
Name: "client-agent-port",
Target: &c.FlagClientAgentPort,
Default: 9300,
Expand Down
140 changes: 38 additions & 102 deletions internal/cmd/base/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,70 +98,6 @@ func (b *boolValue) Example() string { return "" }
func (b *boolValue) Hidden() bool { return b.hidden }
func (b *boolValue) IsBoolFlag() bool { return true }

// -- IntVar and intValue
type IntVar struct {
Name string
Aliases []string
Usage string
Default int
Hidden bool
EnvVar string
Target *int
Completion complete.Predictor
}

func (f *FlagSet) IntVar(i *IntVar) {
initial := i.Default
if v, exist := os.LookupEnv(i.EnvVar); exist {
if i, err := strconv.ParseInt(v, 0, 64); err == nil {
initial = int(i)
}
}

def := ""
if i.Default != 0 {
def = strconv.FormatInt(int64(i.Default), 10)
}

f.VarFlag(&VarFlag{
Name: i.Name,
Aliases: i.Aliases,
Usage: i.Usage,
Default: def,
EnvVar: i.EnvVar,
Value: newIntValue(initial, i.Target, i.Hidden),
Completion: i.Completion,
})
}

type intValue struct {
hidden bool
target *int
}

func newIntValue(def int, target *int, hidden bool) *intValue {
*target = def
return &intValue{
hidden: hidden,
target: target,
}
}

func (i *intValue) Set(s string) error {
v, err := strconv.ParseInt(s, 0, 64)
if err != nil {
return err
}

*i.target = int(v)
return nil
}

func (i *intValue) Get() any { return int(*i.target) }
func (i *intValue) String() string { return strconv.Itoa(int(*i.target)) }
func (i *intValue) Example() string { return "int" }
func (i *intValue) Hidden() bool { return i.hidden }

// -- Int64Var and int64Value
type Int64Var struct {
Name string
Expand Down Expand Up @@ -226,29 +162,29 @@ func (i *int64Value) String() string { return strconv.FormatInt(int64(*i.target
func (i *int64Value) Example() string { return "int" }
func (i *int64Value) Hidden() bool { return i.hidden }

// -- UintVar && uintValue
type UintVar struct {
// -- Uint64Var and uint64Value
type Uint64Var struct {
Name string
Aliases []string
Usage string
Default uint
Default uint64
Hidden bool
EnvVar string
Target *uint
Target *uint64
Completion complete.Predictor
}

func (f *FlagSet) UintVar(i *UintVar) {
func (f *FlagSet) Uint64Var(i *Uint64Var) {
initial := i.Default
if v, exist := os.LookupEnv(i.EnvVar); exist {
if i, err := strconv.ParseUint(v, 0, 64); err == nil {
initial = uint(i)
initial = i
}
}

def := ""
if i.Default != 0 {
def = strconv.FormatUint(uint64(i.Default), 10)
strconv.FormatUint(i.Default, 10)
}

f.VarFlag(&VarFlag{
Expand All @@ -257,62 +193,62 @@ func (f *FlagSet) UintVar(i *UintVar) {
Usage: i.Usage,
Default: def,
EnvVar: i.EnvVar,
Value: newUintValue(initial, i.Target, i.Hidden),
Value: newUint64Value(initial, i.Target, i.Hidden),
Completion: i.Completion,
})
}

type uintValue struct {
type uint64Value struct {
hidden bool
target *uint
target *uint64
}

func newUintValue(def uint, target *uint, hidden bool) *uintValue {
func newUint64Value(def uint64, target *uint64, hidden bool) *uint64Value {
*target = def
return &uintValue{
return &uint64Value{
hidden: hidden,
target: target,
}
}

func (i *uintValue) Set(s string) error {
func (i *uint64Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
if err != nil {
return err
}

*i.target = uint(v)
*i.target = v
return nil
}

func (i *uintValue) Get() any { return uint(*i.target) }
func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i.target), 10) }
func (i *uintValue) Example() string { return "uint" }
func (i *uintValue) Hidden() bool { return i.hidden }
func (i *uint64Value) Get() any { return uint64(*i.target) }
func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i.target), 10) }
func (i *uint64Value) Example() string { return "uint" }
func (i *uint64Value) Hidden() bool { return i.hidden }

// -- Uint64Var and uint64Value
type Uint64Var struct {
// -- Uint16Var and uint16Value
type Uint16Var struct {
Name string
Aliases []string
Usage string
Default uint64
Default uint16
Hidden bool
EnvVar string
Target *uint64
Target *uint16
Completion complete.Predictor
}

func (f *FlagSet) Uint64Var(i *Uint64Var) {
func (f *FlagSet) Uint16Var(i *Uint16Var) {
initial := i.Default
if v, exist := os.LookupEnv(i.EnvVar); exist {
if i, err := strconv.ParseUint(v, 0, 64); err == nil {
initial = i
if i, err := strconv.ParseUint(v, 0, 16); err == nil {
initial = uint16(i)
}
}

def := ""
if i.Default != 0 {
strconv.FormatUint(i.Default, 10)
strconv.FormatUint(uint64(i.Default), 10)
}

f.VarFlag(&VarFlag{
Expand All @@ -321,38 +257,38 @@ func (f *FlagSet) Uint64Var(i *Uint64Var) {
Usage: i.Usage,
Default: def,
EnvVar: i.EnvVar,
Value: newUint64Value(initial, i.Target, i.Hidden),
Value: newUint16Value(initial, i.Target, i.Hidden),
Completion: i.Completion,
})
}

type uint64Value struct {
type uint16Value struct {
hidden bool
target *uint64
target *uint16
}

func newUint64Value(def uint64, target *uint64, hidden bool) *uint64Value {
func newUint16Value(def uint16, target *uint16, hidden bool) *uint16Value {
*target = def
return &uint64Value{
return &uint16Value{
hidden: hidden,
target: target,
}
}

func (i *uint64Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 64)
func (i *uint16Value) Set(s string) error {
v, err := strconv.ParseUint(s, 0, 16)
if err != nil {
return err
}

*i.target = v
*i.target = uint16(v)
return nil
}

func (i *uint64Value) Get() any { return uint64(*i.target) }
func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i.target), 10) }
func (i *uint64Value) Example() string { return "uint" }
func (i *uint64Value) Hidden() bool { return i.hidden }
func (i *uint16Value) Get() any { return uint64(*i.target) }
func (i *uint16Value) String() string { return strconv.FormatUint(uint64(*i.target), 10) }
func (i *uint16Value) Example() string { return "uint" }
func (i *uint16Value) Hidden() bool { return i.hidden }

// -- StringVar and stringValue
type StringVar struct {
Expand Down
Loading
Loading