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, ¶ms) 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 ' 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) }