Skip to content

Commit

Permalink
adding support for CLI reservations
Browse files Browse the repository at this point in the history
  • Loading branch information
ajones committed Oct 6, 2021
1 parent 95c391f commit 39d9cd7
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 53 deletions.
3 changes: 2 additions & 1 deletion cmd/kubefwd/kubefwd.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ func newRootCmd() *cobra.Command {
" kubefwd svc -n default -l \"app in (ws, api)\"\n" +
" kubefwd svc -n default -n the-project\n" +
" kubefwd svc -n the-project -m 80:8080 -m 443:1443\n" +
" kubefwd svc -n the-project -s path/to/conf.yml\n" +
" kubefwd svc -n the-project -z path/to/conf.yml\n" +
" kubefwd svc -n the-project -r ctx.ns.svc:127.3.3.1\n" +
" kubefwd svc --all-namespaces",

Long: globalUsage,
Expand Down
44 changes: 24 additions & 20 deletions cmd/kubefwd/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ var verbose bool
var domain string
var mappings []string
var isAllNs bool
var svcConfigurationPath string
var fwdConfigurationPath string
var fwdReservations []string

func init() {
// override error output from k8s.io/apimachinery/pkg/util/runtime
Expand All @@ -74,7 +75,8 @@ func init() {
Cmd.Flags().StringVarP(&domain, "domain", "d", "", "Append a pseudo domain name to generated host names.")
Cmd.Flags().StringSliceVarP(&mappings, "mapping", "m", []string{}, "Specify a port mapping. Specify multiple mapping by duplicating this argument.")
Cmd.Flags().BoolVarP(&isAllNs, "all-namespaces", "A", false, "Enable --all-namespaces option like kubectl.")
Cmd.Flags().StringVarP(&svcConfigurationPath, "fwd-conf", "z", "", "Define a forward configuration map")
Cmd.Flags().StringSliceVarP(&fwdReservations, "reserve", "r", []string{}, "Specify an IP reservation. Specify multiple reservations by duplicating this argument.")
Cmd.Flags().StringVarP(&fwdConfigurationPath, "fwd-conf", "z", "", "Define an IP reservation configuration")

}

Expand All @@ -91,6 +93,7 @@ var Cmd = &cobra.Command{
" kubefwd svc -n the-project -x prod-cluster\n" +
" kubefwd svc -n the-project -m 80:8080 -m 443:1443\n" +
" kubefwd svc -n the-project -z path/to/conf.yml\n" +
" kubefwd svc -n the-project -r ctx.ns.svc:127.3.3.1\n" +
" kubefwd svc --all-namespaces",
Run: runCmd,
}
Expand Down Expand Up @@ -425,24 +428,25 @@ func (opts *NamespaceOpts) AddServiceHandler(obj interface{}) {

// Define a service to forward
svcfwd := &fwdservice.ServiceFWD{
ClientSet: opts.ClientSet,
Context: opts.Context,
Namespace: opts.Namespace,
Hostfile: opts.HostFile,
ClientConfig: opts.ClientConfig,
RESTClient: opts.RESTClient,
NamespaceN: opts.NamespaceN,
ClusterN: opts.ClusterN,
Domain: opts.Domain,
PodLabelSelector: selector,
NamespaceServiceLock: opts.NamespaceIPLock,
Svc: svc,
Headless: svc.Spec.ClusterIP == "None",
PortForwards: make(map[string]*fwdport.PortForwardOpts),
SyncDebouncer: debounce.New(5 * time.Second),
DoneChannel: make(chan struct{}),
PortMap: opts.ParsePortMap(mappings),
ServiceConfigPath: svcConfigurationPath,
ClientSet: opts.ClientSet,
Context: opts.Context,
Namespace: opts.Namespace,
Hostfile: opts.HostFile,
ClientConfig: opts.ClientConfig,
RESTClient: opts.RESTClient,
NamespaceN: opts.NamespaceN,
ClusterN: opts.ClusterN,
Domain: opts.Domain,
PodLabelSelector: selector,
NamespaceServiceLock: opts.NamespaceIPLock,
Svc: svc,
Headless: svc.Spec.ClusterIP == "None",
PortForwards: make(map[string]*fwdport.PortForwardOpts),
SyncDebouncer: debounce.New(5 * time.Second),
DoneChannel: make(chan struct{}),
PortMap: opts.ParsePortMap(mappings),
ForwardConfigurationPath: fwdConfigurationPath,
ForwardIPReservations: fwdReservations,
}

// Add the service to the catalog of services being forwarded
Expand Down
9 changes: 3 additions & 6 deletions example.fwdconf.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
baseUnreservedIP: 127.1.27.1
serviceConfigurations:
- # context that will contain this namespace
context: context.name
# namespace expected to hold this service
namespace: foobar
# name of the service you wish to specify the IP for
serviceName: service-name
- # identifier consisting of context name, namespace,
# and service name separated by a period
identifier: context-name.namespace.service-name
# ip address you wish to utilize for this service
ip: 127.1.28.1
73 changes: 49 additions & 24 deletions pkg/fwdIp/fwdIp.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type ForwardIPOpts struct {
Namespace string
Port string
ForwardConfigurationPath string
ForwardIPReservations []string
}

// Registry is a structure to create and hold all of the
Expand All @@ -33,15 +34,13 @@ type Registry struct {
}

type ForwardConfiguration struct {
BaseUnreservedIP string `yaml:"baseUnreservedIP"`
ServiceConfigurations []ServiceConfiguration `yaml:"serviceConfigurations"`
BaseUnreservedIP string `yaml:"baseUnreservedIP"`
ServiceConfigurations []*ServiceConfiguration `yaml:"serviceConfigurations"`
}

type ServiceConfiguration struct {
Context string `yaml:"context"`
Namespace string `yaml:"namespace"`
ServiceName string `yaml:"serviceName"`
IP string `yaml:"ip"`
Identifier string `yaml:"identifier"`
IP string `yaml:"ip"`
}

var ipRegistry *Registry
Expand Down Expand Up @@ -73,7 +72,7 @@ func GetIp(opts ForwardIPOpts) (net.IP, error) {
}

func determineIP(regKey string, opts ForwardIPOpts) net.IP {
baseUnreservedIP := getBaseUnreservedIP(opts.ForwardConfigurationPath)
baseUnreservedIP := getBaseUnreservedIP(opts)

// if a configuration exists use it
svcConf := getConfigurationForService(opts)
Expand Down Expand Up @@ -149,8 +148,8 @@ func ipFromString(ipStr string) (net.IP, error) {
return net.IP{byte(octet0), byte(octet1), byte(octet2), byte(octet3)}.To4(), nil
}

func getBaseUnreservedIP(forwardConfigurationPath string) []byte {
fwdCfg := getForwardConfiguration(forwardConfigurationPath)
func getBaseUnreservedIP(opts ForwardIPOpts) []byte {
fwdCfg := getForwardConfiguration(opts)
ip, err := ipFromString(fwdCfg.BaseUnreservedIP)
if err != nil {
panic(err)
Expand All @@ -159,49 +158,75 @@ func getBaseUnreservedIP(forwardConfigurationPath string) []byte {
}

func getConfigurationForService(opts ForwardIPOpts) *ServiceConfiguration {
fwdCfg := getForwardConfiguration(opts.ForwardConfigurationPath)
fwdCfg := getForwardConfiguration(opts)

for _, c := range fwdCfg.ServiceConfigurations {
if c.ServiceName == opts.ServiceName &&
c.Namespace == opts.Namespace &&
c.Context == opts.Context {
return &c
toMatch := fmt.Sprintf("%s.%s.%s", opts.Context, opts.Namespace, opts.ServiceName)
if c.Identifier == toMatch {
return c
}
}
return nil
}

func getForwardConfiguration(forwardConfigurationPath string) *ForwardConfiguration {
func applyCLIPassedReservations(opts ForwardIPOpts, f *ForwardConfiguration) *ForwardConfiguration {
for _, resStr := range opts.ForwardIPReservations {
parts := strings.Split(resStr, ":")
if len(parts) != 2 {
continue // invalid syntax
}
// find any existing
identifier := parts[0]
ipStr := parts[1]
overridden := false
for _, c := range f.ServiceConfigurations {
if c.Identifier == identifier {
c.IP = ipStr
overridden = true
log.Infof("cli reservation flag overriding config for %s now %s", c.Identifier, c.IP)
}
}
if !overridden {
f.ServiceConfigurations = append(f.ServiceConfigurations, &ServiceConfiguration{
Identifier: identifier,
IP: ipStr,
})
}
}
return f
}

func getForwardConfiguration(opts ForwardIPOpts) *ForwardConfiguration {
if forwardConfiguration != nil {
return forwardConfiguration
}

if forwardConfigurationPath == "" {
if opts.ForwardConfigurationPath == "" {
forwardConfiguration = defaultConfiguration
return forwardConfiguration
return applyCLIPassedReservations(opts, forwardConfiguration)
}

dat, err := os.ReadFile(forwardConfigurationPath)
dat, err := os.ReadFile(opts.ForwardConfigurationPath)
if err != nil {
// fall back to existing kubefwd base
log.Error(fmt.Sprintf("ForwardConfiguration read error %s", err))
log.Errorf("ForwardConfiguration read error %s", err)
forwardConfiguration = defaultConfiguration
return forwardConfiguration
return applyCLIPassedReservations(opts, forwardConfiguration)
}

conf := &ForwardConfiguration{}
err = yaml.Unmarshal(dat, conf)
if err != nil {
// fall back to existing kubefwd base
log.Error(fmt.Sprintf("ForwardConfiguration parse error %s", err))
log.Errorf("ForwardConfiguration parse error %s", err)
forwardConfiguration = defaultConfiguration
return forwardConfiguration
return applyCLIPassedReservations(opts, forwardConfiguration)
}

forwardConfiguration = conf
return forwardConfiguration
return applyCLIPassedReservations(opts, forwardConfiguration)
}

func (c ServiceConfiguration) String() string {
return fmt.Sprintf("Ctx: %s Ns:%s Svc:%s IP:%s", c.Context, c.Namespace, c.ServiceName, c.IP)
return fmt.Sprintf("ID: %s IP:%s", c.Identifier, c.IP)
}
6 changes: 4 additions & 2 deletions pkg/fwdservice/fwdservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ type ServiceFWD struct {
PortForwards map[string]*fwdport.PortForwardOpts
DoneChannel chan struct{} // After shutdown is complete, this channel will be closed

ServiceConfigPath string
ForwardConfigurationPath string // file path to IP reservation configuration
ForwardIPReservations []string // cli passed IP reservations
}

/**
Expand Down Expand Up @@ -243,7 +244,8 @@ func (svcFwd *ServiceFWD) LoopPodsToForward(pods []v1.Pod, includePodNameInHost
NamespaceN: svcFwd.NamespaceN,
Namespace: svcFwd.Namespace,
Port: podPort,
ForwardConfigurationPath: svcFwd.ServiceConfigPath,
ForwardConfigurationPath: svcFwd.ForwardConfigurationPath,
ForwardIPReservations: svcFwd.ForwardIPReservations,
}
localIp, err := fwdnet.ReadyInterface(opts)
if err != nil {
Expand Down

0 comments on commit 39d9cd7

Please sign in to comment.