1
0
Fork 0
mirror of https://github.com/pomerium/pomerium.git synced 2025-07-10 13:28:36 +02:00
pomerium/internal/zero/cmd/command_import.go
Joe Kralicky fe31799eb5
Fix many instances of contexts and loggers not being propagated ()
This also replaces instances where we manually write "return ctx.Err()"
with "return context.Cause(ctx)" which is functionally identical, but
will also correctly propagate cause errors if present.
2024-10-25 14:50:56 -04:00

176 lines
4.5 KiB
Go

package cmd
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path"
"strconv"
"strings"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/pkg/envoy/files"
"github.com/pomerium/pomerium/pkg/zero/cluster"
"github.com/pomerium/pomerium/pkg/zero/importutil"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)
func BuildImportCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "import",
Short: "Import an existing configuration to a Zero cluster",
RunE: func(cmd *cobra.Command, _ []string) error {
configFlag := cmd.InheritedFlags().Lookup("config")
var configFile string
if configFlag != nil {
configFile = configFlag.Value.String()
}
envInfo := findEnvironmentInfo()
if configFile == "" {
configFile = envInfo.ConfigArg
}
if configFile == "" {
return fmt.Errorf("no config file provided")
}
log.SetLevel(zerolog.ErrorLevel)
src, err := config.NewFileOrEnvironmentSource(cmd.Context(), configFile, files.FullVersion())
if err != nil {
return err
}
cfg := src.GetConfig()
client := zeroClientFromContext(cmd.Context())
converted := cfg.Options.ToProto()
for i, name := range importutil.GenerateRouteNames(converted.Routes) {
converted.Routes[i].Name = name
}
var params cluster.ImportConfigurationParams
if data, err := json.Marshal(envInfo); err == nil {
hints := make(map[string]string)
if err := json.Unmarshal(data, &hints); err == nil {
pairs := []string{}
for key, value := range hints {
pairs = append(pairs, fmt.Sprintf("%s=%s", key, value))
}
if len(pairs) > 0 {
params.XImportHints = &pairs
}
}
}
resp, err := client.ImportConfig(cmd.Context(), converted, &params)
if err != nil {
return fmt.Errorf("error importing config: %w", err)
}
if resp.Warnings != nil {
for _, warn := range *resp.Warnings {
cmd.Printf("warning: %s\n", warn)
}
}
if resp.Messages != nil {
for _, msg := range *resp.Messages {
cmd.Printf("✔ %s\n", msg)
}
}
cmd.Println("\nImport successful, return to your browser to continue setup.")
return nil
},
}
return cmd
}
type environmentInfo struct {
SystemType string `json:"systemType,omitempty"`
Hostname string `json:"hostname,omitempty"`
KubernetesNamespace string `json:"kubernetesNamespace,omitempty"`
Argv0 string `json:"argv0,omitempty"`
ConfigArg string `json:"configArg,omitempty"`
}
func findEnvironmentInfo() environmentInfo {
var info environmentInfo
if isKubernetes() {
info.SystemType = "kubernetes"
// search for downward api environment variables to see if we can determine
// the current namespace (adds '-n <namespace>' to the command given in the
// zero ui for users to copy/paste)
for _, env := range []string{
"POMERIUM_NAMESPACE", // the name we use in our official manifests
"POD_NAMESPACE", // very common alternative name
} {
if v, ok := os.LookupEnv(env); ok {
info.KubernetesNamespace = v
break
}
}
} else if isDocker() {
info.SystemType = "docker"
} else {
info.SystemType = "linux"
info.Argv0 = os.Args[0]
return info
}
info.Hostname, _ = os.Hostname()
pid, ok := findPomeriumPid()
if !ok {
return info
}
cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
if err != nil {
return info
}
args := bytes.Split(cmdline, []byte{0})
if len(args) > 0 {
info.Argv0 = string(args[0])
}
for i, arg := range args {
if strings.Contains(string(arg), "-config") {
if strings.Contains(string(arg), "-config=") {
info.ConfigArg = strings.Split(string(arg), "=")[1]
} else if len(args) > i+1 {
info.ConfigArg = string(args[i+1])
}
}
}
return info
}
func isKubernetes() bool {
return os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != ""
}
func isDocker() bool {
_, err := os.Stat("/.dockerenv")
return err == nil
}
func findPomeriumPid() (int, bool) {
pid1Argv0 := getProcessArgv0(1)
if path.Base(pid1Argv0) == "pomerium" {
return 1, true
}
pidList, err := os.ReadFile("/proc/1/task/1/children")
if err != nil {
return 0, false
}
for _, pidStr := range strings.Fields(string(pidList)) {
pid, _ := strconv.Atoi(pidStr)
if path.Base(getProcessArgv0(pid)) == "pomerium" {
return pid, true
}
}
return 0, false
}
func getProcessArgv0(pid int) string {
cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
if err != nil {
return ""
}
argv0, _, _ := bytes.Cut(cmdline, []byte{0})
return string(argv0)
}