|
4 | 4 | package cmd |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "fmt" |
| 8 | + "log" |
| 9 | + "os" |
| 10 | + "os/exec" |
| 11 | + "path/filepath" |
| 12 | + "runtime" |
| 13 | + "slices" |
| 14 | + |
7 | 15 | "github.com/codesphere-cloud/cs-go/pkg/io" |
| 16 | + "github.com/codesphere-cloud/oms/internal/env" |
| 17 | + "github.com/codesphere-cloud/oms/internal/installer" |
| 18 | + "github.com/codesphere-cloud/oms/internal/util" |
8 | 19 | "github.com/spf13/cobra" |
9 | 20 | ) |
10 | 21 |
|
11 | 22 | // InstallCodesphereCmd represents the codesphere command |
12 | 23 | type InstallCodesphereCmd struct { |
13 | | - cmd *cobra.Command |
| 24 | + cmd *cobra.Command |
| 25 | + Opts *InstallCodesphereOpts |
| 26 | + Env env.Env |
| 27 | +} |
| 28 | + |
| 29 | +type InstallCodesphereOpts struct { |
| 30 | + *GlobalOptions |
| 31 | + Package string |
| 32 | + Force bool |
| 33 | + Config string |
| 34 | + PrivKey string |
| 35 | + SkipSteps []string |
| 36 | +} |
| 37 | + |
| 38 | +func (c *InstallCodesphereCmd) RunE(_ *cobra.Command, args []string) error { |
| 39 | + workdir := c.Env.GetOmsWorkdir() |
| 40 | + p := installer.NewPackage(workdir, c.Opts.Package) |
| 41 | + |
| 42 | + err := c.ExtractAndInstall(p, runtime.GOOS, runtime.GOARCH) |
| 43 | + if err != nil { |
| 44 | + return fmt.Errorf("failed to extract and install package: %w", err) |
| 45 | + } |
| 46 | + |
| 47 | + return nil |
14 | 48 | } |
15 | 49 |
|
16 | | -func AddInstallCodesphereCmd(install *cobra.Command) { |
| 50 | +func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { |
17 | 51 | codesphere := InstallCodesphereCmd{ |
18 | 52 | cmd: &cobra.Command{ |
19 | 53 | Use: "codesphere", |
20 | | - Short: "Coming soon: Install a Codesphere instance", |
21 | | - Long: io.Long(`Coming soon: Install a Codesphere instance`), |
| 54 | + Short: "Install a Codesphere instance", |
| 55 | + Long: io.Long(`Install a Codesphere instance with the provided package, configuration file, and private key. |
| 56 | + Uses the private-cloud-installer.js script included in the package to perform the installation.`), |
22 | 57 | }, |
| 58 | + Opts: &InstallCodesphereOpts{GlobalOptions: opts}, |
| 59 | + Env: env.NewEnv(), |
23 | 60 | } |
| 61 | + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load binaries, installer etc. from") |
| 62 | + codesphere.cmd.Flags().BoolVarP(&codesphere.Opts.Force, "force", "f", false, "Enforce package extraction") |
| 63 | + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.Config, "config", "c", "", "Path to the Codesphere Private Cloud configuration file (yaml)") |
| 64 | + codesphere.cmd.Flags().StringVarP(&codesphere.Opts.PrivKey, "priv-key", "k", "", "Path to the private key to encrypt/decrypt secrets") |
| 65 | + codesphere.cmd.Flags().StringSliceVarP(&codesphere.Opts.SkipSteps, "skip-steps", "s", []string{}, "Steps to be skipped. Must be one of: copy-dependencies, extract-dependencies, load-container-images, ceph, kubernetes") |
| 66 | + |
| 67 | + util.MarkFlagRequired(codesphere.cmd, "package") |
| 68 | + util.MarkFlagRequired(codesphere.cmd, "config") |
| 69 | + util.MarkFlagRequired(codesphere.cmd, "priv-key") |
| 70 | + |
24 | 71 | install.AddCommand(codesphere.cmd) |
| 72 | + codesphere.cmd.RunE = codesphere.RunE |
| 73 | +} |
| 74 | + |
| 75 | +func (c *InstallCodesphereCmd) ExtractAndInstall(p *installer.Package, goos string, goarch string) error { |
| 76 | + if goos != "linux" || goarch != "amd64" { |
| 77 | + return fmt.Errorf("codesphere installation is only supported on Linux amd64. Current platform: %s/%s", goos, goarch) |
| 78 | + } |
| 79 | + |
| 80 | + err := p.Extract(c.Opts.Force) |
| 81 | + if err != nil { |
| 82 | + return fmt.Errorf("failed to extract package to workdir: %w", err) |
| 83 | + } |
| 84 | + |
| 85 | + foundFiles, err := c.ListPackageContents(p) |
| 86 | + if err != nil { |
| 87 | + return fmt.Errorf("failed to list available files: %w", err) |
| 88 | + } |
| 89 | + |
| 90 | + if !slices.Contains(foundFiles, "deps.tar.gz") { |
| 91 | + return fmt.Errorf("deps.tar.gz not found in package") |
| 92 | + } |
| 93 | + if !slices.Contains(foundFiles, "private-cloud-installer.js") { |
| 94 | + return fmt.Errorf("private-cloud-installer.js not found in package") |
| 95 | + } |
| 96 | + if !slices.Contains(foundFiles, "node") { |
| 97 | + return fmt.Errorf("node executable not found in package") |
| 98 | + } |
| 99 | + |
| 100 | + nodePath := filepath.Join(".", p.GetWorkDir(), "node") |
| 101 | + err = os.Chmod(nodePath, 0755) |
| 102 | + if err != nil { |
| 103 | + return fmt.Errorf("failed to make node executable: %w", err) |
| 104 | + } |
| 105 | + |
| 106 | + log.Printf("Using Node.js executable: %s", nodePath) |
| 107 | + log.Println("Starting private cloud installer script...") |
| 108 | + installerPath := filepath.Join(".", p.GetWorkDir(), "private-cloud-installer.js") |
| 109 | + archivePath := filepath.Join(".", p.GetWorkDir(), "deps.tar.gz") |
| 110 | + |
| 111 | + // Build command |
| 112 | + cmdArgs := []string{installerPath, "--archive", archivePath, "--config", c.Opts.Config, "--privKey", c.Opts.PrivKey} |
| 113 | + if len(c.Opts.SkipSteps) > 0 { |
| 114 | + for _, step := range c.Opts.SkipSteps { |
| 115 | + cmdArgs = append(cmdArgs, "--skipStep", step) |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + cmd := exec.Command(nodePath, cmdArgs...) |
| 120 | + cmd.Stdout = os.Stdout |
| 121 | + cmd.Stderr = os.Stderr |
| 122 | + cmd.Stdin = os.Stdin |
| 123 | + |
| 124 | + err = cmd.Run() |
| 125 | + if err != nil { |
| 126 | + return fmt.Errorf("failed to run installer script: %w", err) |
| 127 | + } |
| 128 | + log.Println("Private cloud installer script finished.") |
| 129 | + |
| 130 | + return nil |
| 131 | +} |
| 132 | + |
| 133 | +func (c *InstallCodesphereCmd) ListPackageContents(p *installer.Package) ([]string, error) { |
| 134 | + packageDir := p.GetWorkDir() |
| 135 | + if !p.FileIO.Exists(packageDir) { |
| 136 | + return nil, fmt.Errorf("work dir not found: %s", packageDir) |
| 137 | + } |
| 138 | + |
| 139 | + entries, err := p.FileIO.ReadDir(packageDir) |
| 140 | + if err != nil { |
| 141 | + return nil, fmt.Errorf("failed to read directory contents: %w", err) |
| 142 | + } |
| 143 | + |
| 144 | + log.Printf("Listing contents of %s", packageDir) |
| 145 | + var foundFiles []string |
| 146 | + for _, entry := range entries { |
| 147 | + filename := entry.Name() |
| 148 | + log.Printf("- %s", filename) |
| 149 | + foundFiles = append(foundFiles, filename) |
| 150 | + } |
| 151 | + |
| 152 | + return foundFiles, nil |
25 | 153 | } |
0 commit comments