185 lines
5.6 KiB
Go

// config.go
//
// This source file is part of the FoundationDB open source project
//
// Copyright 2021 Apple Inc. and the FoundationDB project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package api
import (
"fmt"
"net"
"os"
"strconv"
"strings"
)
// ProcessConfiguration models the configuration for starting a FoundationDB
// process.
type ProcessConfiguration struct {
// Version provides the version of FoundationDB the process should run.
Version string `json:"version"`
// RunServers defines whether we should run the server processes.
// This defaults to true, but you can set it to false to prevent starting
// new fdbserver processes.
RunServers *bool `json:"runServers,omitempty"`
// BinaryPath provides the path to the binary to launch.
BinaryPath string `json:"-"`
// Arguments provides the arguments to the process.
Arguments []Argument `json:"arguments,omitempty"`
}
// Argument defines an argument to the process.
type Argument struct {
// ArgumentType determines how the value is generated.
ArgumentType ArgumentType `json:"type,omitempty"`
// Value provides the value for a Literal type argument.
Value string `json:"value,omitempty"`
// Values provides the sub-values for a Concatenate type argument.
Values []Argument `json:"values,omitempty"`
// Source provides the name of the environment variable to use for an
// Environment type argument.
Source string `json:"source,omitempty"`
// Multiplier provides a multiplier for the process number for ProcessNumber
// type arguments.
Multiplier int `json:"multiplier,omitempty"`
// Offset provides an offset to add to the process number for ProcessNumber
// type arguments.
Offset int `json:"offset,omitempty"`
// IPFamily provides the family to use for IPList type arguments.
IPFamily int `json:"ipFamily,omitempty"`
}
// ArgumentType defines the types for arguments.
type ArgumentType string
const (
// LiteralArgumentType defines an argument with a literal string value.
LiteralArgumentType ArgumentType = "Literal"
// ConcatenateArgumentType defines an argument composed of other arguments.
ConcatenateArgumentType = "Concatenate"
// EnvironmentArgumentType defines an argument that is pulled from an
// environment variable.
EnvironmentArgumentType = "Environment"
// ProcessNumberArgumentType defines an argument that is calculated using
// the number of the process in the process list.
ProcessNumberArgumentType = "ProcessNumber"
// IPListArgumentType defines an argument that is a comma-separated list of
// IP addresses, provided through an environment variable.
IPListArgumentType = "IPList"
)
// GenerateArgument processes an argument and generates its string
// representation.
func (argument Argument) GenerateArgument(processNumber int, env map[string]string) (string, error) {
switch argument.ArgumentType {
case "":
fallthrough
case LiteralArgumentType:
return argument.Value, nil
case ConcatenateArgumentType:
concatenated := ""
for _, childArgument := range argument.Values {
childValue, err := childArgument.GenerateArgument(processNumber, env)
if err != nil {
return "", err
}
concatenated += childValue
}
return concatenated, nil
case ProcessNumberArgumentType:
number := processNumber
if argument.Multiplier != 0 {
number = number * argument.Multiplier
}
number = number + argument.Offset
return strconv.Itoa(number), nil
case EnvironmentArgumentType, IPListArgumentType:
return argument.LookupEnv(env)
default:
return "", fmt.Errorf("unsupported argument type %s", argument.ArgumentType)
}
}
// LookupEnv looks up the value for an argument from the environment.
func (argument Argument) LookupEnv(env map[string]string) (string, error) {
var value string
var present bool
if env != nil {
value, present = env[argument.Source]
}
if !present {
value, present = os.LookupEnv(argument.Source)
}
if !present {
return "", fmt.Errorf("missing environment variable %s", argument.Source)
}
if argument.ArgumentType == IPListArgumentType {
ips := strings.Split(value, ",")
for _, ipString := range ips {
ip := net.ParseIP(ipString)
if ip == nil {
continue
}
switch argument.IPFamily {
case 4:
if ip.To4() != nil {
return ipString, nil
}
case 6:
if ip.To16() != nil && ip.To4() == nil {
return ipString, nil
}
default:
return "", fmt.Errorf("unsupported IP family %d", argument.IPFamily)
}
}
return "", fmt.Errorf("could not find IP with family %d", argument.IPFamily)
}
return value, nil
}
// GenerateArguments interprets the arguments in the process configuration and
// generates a command invocation.
func (configuration *ProcessConfiguration) GenerateArguments(processNumber int, env map[string]string) ([]string, error) {
results := make([]string, 0, len(configuration.Arguments)+1)
if configuration.BinaryPath != "" {
results = append(results, configuration.BinaryPath)
}
for _, argument := range configuration.Arguments {
result, err := argument.GenerateArgument(processNumber, env)
if err != nil {
return nil, err
}
results = append(results, result)
}
return results, nil
}