Skip to content

Commit

Permalink
adding env package
Browse files Browse the repository at this point in the history
  • Loading branch information
dgkanatsios committed Aug 6, 2022
1 parent 3dcaca5 commit be6550c
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 99 deletions.
6 changes: 3 additions & 3 deletions cmd/initcontainer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ func main() {
// parseBuildMetadata parses the build metadata from the corresponding environment variable
func parseBuildMetadata() map[string]string {
buildMetadata := make(map[string]string)
if os.Getenv("PF_GAMESERVER_BUILD_METADATA") != "" {
metadata := os.Getenv("PF_GAMESERVER_BUILD_METADATA")
s := strings.Split(metadata, "?")
envMetadata := os.Getenv("PF_GAMESERVER_BUILD_METADATA")
if envMetadata != "" {
s := strings.Split(envMetadata, "?")
for _, s2 := range s {
if s2 == "" {
continue
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/playfab/thundernetes
go 1.18

require (
github.com/caarlos0/env/v6 v6.9.3
github.com/gin-gonic/gin v1.7.4
github.com/go-logr/logr v1.2.0
github.com/google/uuid v1.2.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/caarlos0/env/v6 v6.9.3 h1:Tyg69hoVXDnpO5Qvpsu8EoquarbPyQb+YwExWHP8wWU=
github.com/caarlos0/env/v6 v6.9.3/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
Expand Down
19 changes: 0 additions & 19 deletions pkg/operator/controllers/controller_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ package controllers
import (
"bytes"
"context"
"errors"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"

"github.com/go-logr/logr"
mpsv1alpha1 "github.com/playfab/thundernetes/pkg/operator/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -459,19 +456,3 @@ func getValueByState(gs *mpsv1alpha1.GameServer) int {
return 3
}
}

// GetInitContainerImages returns the init container images from the environment variables
func GetInitContainerImages(l logr.Logger) (string, string) {
initContainerImageLinux := os.Getenv("THUNDERNETES_INIT_CONTAINER_IMAGE")
if initContainerImageLinux == "" {
l.Error(errors.New("THUNDERNETES_INIT_CONTAINER_IMAGE is not set, setting to a mock value"), "")
initContainerImageLinux = "testInitContainerImage"
}
initContainerImageWin := os.Getenv("THUNDERNETES_INIT_CONTAINER_IMAGE_WIN")
if initContainerImageWin == "" {
l.Error(errors.New("THUNDERNETES_INIT_CONTAINER_IMAGE_WIN is not set, setting to a mock value"), "")
initContainerImageWin = "testInitContainerImageWin"
}
l.Info("init container images", "linux", initContainerImageLinux, "win", initContainerImageWin)
return initContainerImageLinux, initContainerImageWin
}
2 changes: 1 addition & 1 deletion pkg/operator/controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ var _ = BeforeSuite(func() {
err = portRegistry.SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

initContainerImageLinux, initContainerImageWin := GetInitContainerImages(z)
initContainerImageLinux, initContainerImageWin := "testImageLinux", "testImageWin"
Expect(initContainerImageLinux).ToNot(BeEmpty())
Expect(initContainerImageWin).ToNot(BeEmpty())

Expand Down
125 changes: 49 additions & 76 deletions pkg/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import (
"context"
"errors"
"flag"
"log"
"os"
"strconv"
"time"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
"github.com/caarlos0/env/v6"
"github.com/go-logr/logr"
_ "go.uber.org/automaxprocs"
"go.uber.org/zap/zapcore"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -38,8 +40,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

"github.com/go-logr/logr"

mpsv1alpha1 "github.com/playfab/thundernetes/pkg/operator/api/v1alpha1"
"github.com/playfab/thundernetes/pkg/operator/controllers"

Expand All @@ -48,17 +48,27 @@ import (
corev1 "k8s.io/api/core/v1"
)

// Config is a struct containing configuration from environment variables
// source: https://github.com/caarlos0/env
type Config struct {
ApiServiceSecurity string `env:"API_SERVICE_SECURITY"`
TlsSecretName string `env:"TLS_SECRET_NAME" envDefault:"tls-secret"`
TlsSecretNamespace string `env:"TLS_SECRET_NAMESPACE" envDefault:"thundernetes-system"`
TlsCertificateName string `env:"TLS_CERTIFICATE_FILENAME" envDefault:"tls.crt"`
TlsPrivateKeyFilename string `env:"TLS_PRIVATE_KEY_FILENAME" envDefault:"tls.key"`
PortRegistryExclusivelyGameServerNodes bool `env:"PORT_REGISTRY_EXCLUSIVELY_GAME_SERVER_NODES" envDefault:"false"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
MinPort int32 `env:"MIN_PORT" envDefault:"10000"`
MaxPort int32 `env:"MAX_PORT" envDefault:"12000"`
InitContainerImageLinux string `env:"THUNDERNETES_INIT_CONTAINER_IMAGE,notEmpty"`
InitContainerImageWin string `env:"THUNDERNETES_INIT_CONTAINER_IMAGE_WIN,notEmpty"`
}

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)

const (
secretName = "tls-secret"
certificateFileName = "tls.crt"
privateKeyFileName = "tls.key"
)

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

Expand All @@ -67,6 +77,13 @@ func init() {
}

func main() {
// load configuration from env variables
cfg := &Config{}
if err := env.Parse(cfg); err != nil {
log.Fatal(err, "Cannot load configuration from environment variables")
}

// load the rest of the configuration from command-line flags
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
Expand All @@ -77,17 +94,17 @@ func main() {
"Enabling this will ensure there is only one active controller manager.")
opts := zap.Options{
Development: true,
Level: getLogLevel(),
Level: getLogLevel(cfg.LogLevel),
// https://github.com/uber-go/zap/issues/661#issuecomment-520686037 and https://github.com/uber-go/zap/issues/485#issuecomment-834021392
TimeEncoder: zapcore.TimeEncoderOfLayout(time.RFC3339),
}
opts.BindFlags(flag.CommandLine)
flag.Parse()

// setupLog is valid after this call
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

// get init container images from environment variables
initContainerImageLinux, initContainerImageWin := controllers.GetInitContainerImages(setupLog)
setupLog.Info("Loaded configuration from environment variables", "config", cfg)

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Expand All @@ -101,11 +118,10 @@ func main() {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}

// initialize a live API client, used for the PortRegistry and fetching the mTLS secret
k8sClient := mgr.GetAPIReader()
// get public and privage key, if enabled
crt, key := getCrtKeyIfTlsEnabled(k8sClient)
// get public and private key, if enabled
crt, key := getCrtKeyIfTlsEnabled(k8sClient, cfg)

// initialize the allocation API service, which is also a controller. So we add it to the manager
aas := controllers.NewAllocationApiServer(crt, key, mgr.GetClient())
Expand All @@ -115,7 +131,7 @@ func main() {
}

// initialize the portRegistry
portRegistry, err := initializePortRegistry(k8sClient, mgr.GetClient(), setupLog)
portRegistry, err := initializePortRegistry(k8sClient, mgr.GetClient(), setupLog, cfg)
if err != nil {
setupLog.Error(err, "unable to initialize portRegistry")
os.Exit(1)
Expand All @@ -133,8 +149,8 @@ func main() {
PortRegistry: portRegistry,
Recorder: mgr.GetEventRecorderFor("GameServer"),
GetNodeDetailsProvider: controllers.GetNodeDetails,
InitContainerImageLinux: initContainerImageLinux,
InitContainerImageWin: initContainerImageWin,
InitContainerImageLinux: cfg.InitContainerImageLinux,
InitContainerImageWin: cfg.InitContainerImageWin,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "GameServer")
os.Exit(1)
Expand Down Expand Up @@ -182,13 +198,13 @@ func main() {
// initializePortRegistry performs some initialization and creates a new PortRegistry struct
// the k8sClient is a live API client and is used to get the existing gameservers and the "Ready" Nodes
// the crClient is the cached controller-runtime client, used to watch for changes to the nodes from inside the PortRegistry
func initializePortRegistry(k8sClient client.Reader, crClient client.Client, setupLog logr.Logger) (*controllers.PortRegistry, error) {
func initializePortRegistry(k8sClient client.Reader, crClient client.Client, setupLog logr.Logger, cfg *Config) (*controllers.PortRegistry, error) {
var gameServers mpsv1alpha1.GameServerList
if err := k8sClient.List(context.Background(), &gameServers); err != nil {
return nil, err
}

useExclusivelyGameServerNodesForPortRegistry := useExclusivelyGameServerNodesForPortRegistry()
useExclusivelyGameServerNodesForPortRegistry := cfg.PortRegistryExclusivelyGameServerNodes

var nodes corev1.NodeList
if err := k8sClient.List(context.Background(), &nodes); err != nil {
Expand All @@ -206,7 +222,7 @@ func initializePortRegistry(k8sClient client.Reader, crClient client.Client, set

// get the min/max port from enviroment variables
// the code does not offer any protection in case the port range changes while game servers are running
minPort, maxPort, err := getMinMaxPortFromEnv()
minPort, maxPort, err := validateMinMaxPort(cfg)
if err != nil {
return nil, err
}
Expand All @@ -223,61 +239,29 @@ func initializePortRegistry(k8sClient client.Reader, crClient client.Client, set

// getTlsSecret returns the TLS secret from the given namespace
// used in the allocation API service
func getTlsSecret(k8sClient client.Reader, namespace string) ([]byte, []byte, error) {
func getTlsSecret(k8sClient client.Reader, cfg *Config) ([]byte, []byte, error) {
var secret corev1.Secret
err := k8sClient.Get(context.Background(), types.NamespacedName{
Name: secretName,
Namespace: namespace,
Name: cfg.TlsSecretName,
Namespace: cfg.TlsSecretNamespace,
}, &secret)
if err != nil {
return nil, nil, err
}
return []byte(secret.Data[certificateFileName]), []byte(secret.Data[privateKeyFileName]), nil
return []byte(secret.Data[cfg.TlsCertificateName]), []byte(secret.Data[cfg.TlsPrivateKeyFilename]), nil
}

// getMinMaxPortFromEnv returns minimum and maximum port from environment variables
func getMinMaxPortFromEnv() (int32, int32, error) {
minPortStr := os.Getenv("MIN_PORT")
maxPortStr := os.Getenv("MAX_PORT")

// if both of them are not set, return default values
if minPortStr == "" && maxPortStr == "" {
setupLog.Info("MIN_PORT and MAX_PORT environment variables are not set. Using default values 10000 and 12000.")
return 10000, 12000, nil
}

if minPortStr == "" {
// this means that MAX_PORT is set, but not MIN_PORT
return 0, 0, errors.New("MIN_PORT env variable is not set")
}
// we use ParseInt insteaf of Atoi because CodeQL triggered this https://codeql.github.com/codeql-query-help/go/go-incorrect-integer-conversion/
minPortParsed, err := strconv.ParseInt(minPortStr, 10, 32)
if err != nil {
return 0, 0, err
}

if maxPortStr == "" {
// this means that MIN_PORT is set, but not MAX_PORT
return 0, 0, errors.New("MAX_PORT env variable is not set")
}
maxPortParsed, err := strconv.ParseInt(maxPortStr, 10, 32)
if err != nil {
return 0, 0, err
}

minPort := int32(minPortParsed)
maxPort := int32(maxPortParsed)

if minPort >= maxPort {
// validateMinMaxPort validates minimum and maximum ports
func validateMinMaxPort(cfg *Config) (int32, int32, error) {
if cfg.MinPort >= cfg.MaxPort {
return 0, 0, errors.New("MIN_PORT cannot be greater or equal than MAX_PORT")
}

return minPort, maxPort, nil
return cfg.MinPort, cfg.MaxPort, nil
}

// getLogLevel returns the log level based on the LOG_LEVEL environment variable
func getLogLevel() zapcore.LevelEnabler {
logLevel := os.Getenv("LOG_LEVEL")
func getLogLevel(logLevel string) zapcore.LevelEnabler {
switch logLevel {
case "debug":
return zapcore.DebugLevel
Expand All @@ -300,16 +284,9 @@ func getLogLevel() zapcore.LevelEnabler {
// for this to happen, user has to set "API_SERVICE_SECURITY" env as "usetls" and set the env "TLS_SECRET_NAMESPACE" with the namespace
// that contains the Kubernetes Secret with the cert
// if any of the mentioned conditions are not set, method returns nil
func getCrtKeyIfTlsEnabled(c client.Reader) ([]byte, []byte) {
apiServiceSecurity := os.Getenv("API_SERVICE_SECURITY")

if apiServiceSecurity == "usetls" {
namespace := os.Getenv("TLS_SECRET_NAMESPACE")
if namespace == "" {
setupLog.Error(errors.New("unable to get TLS_SECRET_NAMESPACE env variable"), "mTLS is enabled, but TLS_SECRET_NAMESPACE is not set")
os.Exit(1)
}
crt, key, err := getTlsSecret(c, namespace)
func getCrtKeyIfTlsEnabled(c client.Reader, cfg *Config) ([]byte, []byte) {
if cfg.ApiServiceSecurity == "usetls" {
crt, key, err := getTlsSecret(c, cfg)
if err != nil {
setupLog.Error(err, "unable to get TLS secret")
os.Exit(1)
Expand All @@ -318,7 +295,3 @@ func getCrtKeyIfTlsEnabled(c client.Reader) ([]byte, []byte) {
}
return nil, nil
}

func useExclusivelyGameServerNodesForPortRegistry() bool {
return os.Getenv("PORT_REGISTRY_EXCLUSIVELY_GAMESERVER_NODES") == "true"
}

0 comments on commit be6550c

Please sign in to comment.