Skip to content
This repository was archived by the owner on Jul 10, 2024. It is now read-only.

Commit 104fa0d

Browse files
Mark Gritterliujed
andauthored
Install a precompiled module from the latest Github release. (#198)
Checks Nginx version, architecture, and operating system. Downloads the file into a temp directory, then moves it to nginx's modules directory and creates a symbolic link. --------- Co-authored-by: Jed Liu <liujed@users.noreply.github.com>
1 parent 417c021 commit 104fa0d

File tree

4 files changed

+491
-12
lines changed

4 files changed

+491
-12
lines changed

cmd/internal/nginx/nginx.go

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package nginx
22

33
import (
4-
"fmt"
5-
64
"github.com/pkg/errors"
75
"github.com/spf13/cobra"
86

97
"github.com/akitasoftware/akita-cli/cmd/internal/pluginloader"
108
"github.com/akitasoftware/akita-cli/integrations/nginx"
9+
"github.com/akitasoftware/akita-cli/printer"
1110
"github.com/akitasoftware/akita-cli/rest"
1211
"github.com/akitasoftware/akita-cli/telemetry"
1312
)
@@ -19,16 +18,21 @@ var (
1918
// Port number that the module will send traffic to
2019
listenPortFlag uint16
2120

22-
// Dedvelopment mode -- dump out traffic locally
21+
// Development mode -- dump out traffic locally
2322
developmentFlag bool
23+
24+
// Dry run for install -- find version but do not install
25+
dryRunFlag bool
26+
27+
// Module destination
28+
moduleDestFlag string
2429
)
2530

2631
var Cmd = &cobra.Command{
2732
Use: "nginx",
2833
Short: "Install or use Akita's NGINX module to collect API traffic.",
2934
SilenceUsage: true,
30-
// TODO: un-hide when ready for use
31-
Hidden: true,
35+
Hidden: false,
3236
}
3337

3438
var CaptureCmd = &cobra.Command{
@@ -40,21 +44,23 @@ var CaptureCmd = &cobra.Command{
4044
}
4145

4246
var InstallCmd = &cobra.Command{
43-
// TODO: substitute in the real name
44-
Use: "xinstall",
47+
Use: "install",
4548
Short: "Download a precompiled NGINX module.",
4649
Long: "Download a precompiled version of akita-nginx-module that matches the currently installed version of NGINX.",
4750
SilenceUsage: true,
4851
RunE: installNginxModule,
4952
}
5053

5154
func init() {
52-
Cmd.PersistentFlags().StringVar(&projectFlag, "project", "", "Your Akita project.")
53-
Cmd.PersistentFlags().Uint16Var(&listenPortFlag, "port", 50080, "The port number on which to listen for connections.")
54-
Cmd.PersistentFlags().BoolVar(&developmentFlag, "dev", false, "Enable development mode; only dumps traffic.")
55-
Cmd.PersistentFlags().MarkHidden("dev")
55+
CaptureCmd.PersistentFlags().StringVar(&projectFlag, "project", "", "Your Akita project.")
56+
CaptureCmd.PersistentFlags().Uint16Var(&listenPortFlag, "port", 50080, "The port number on which to listen for connections.")
57+
CaptureCmd.PersistentFlags().BoolVar(&developmentFlag, "dev", false, "Enable development mode; only dumps traffic.")
58+
CaptureCmd.PersistentFlags().MarkHidden("dev")
5659

5760
Cmd.AddCommand(CaptureCmd)
61+
62+
InstallCmd.PersistentFlags().BoolVar(&dryRunFlag, "dry-run", false, "Determine NGINX version but do not download or install the module.")
63+
InstallCmd.PersistentFlags().StringVar(&moduleDestFlag, "dest", "", "Specify the directory into which to install the module.")
5864
Cmd.AddCommand(InstallCmd)
5965
}
6066

@@ -90,5 +96,24 @@ func captureNginxTraffic(cmd *cobra.Command, args []string) error {
9096
}
9197

9298
func installNginxModule(cmd *cobra.Command, args []string) error {
93-
return fmt.Errorf("This command is not yet implemented.")
99+
err := nginx.InstallModule(&nginx.InstallArgs{
100+
DryRun: dryRunFlag,
101+
})
102+
if err != nil {
103+
var installError *nginx.InstallationError
104+
switch {
105+
case errors.As(err, &installError):
106+
// Log the error, then what the user should do next
107+
printer.Errorf("%v\n", err)
108+
printer.Infof("%v\n", installError.Remedy)
109+
default:
110+
printer.Errorf("Could not determine which NGINX platform and version to support: %v\n", err)
111+
printer.Infof("Please contact support@akitasoftware.com for assistance, or follow the instructions at https://github.com/akitasoftware/akita-nginx-module to install the module by hand.\n")
112+
}
113+
114+
// Report the error here because we don't report it to the root command
115+
telemetry.Error("command execution", err)
116+
117+
}
118+
return nil
94119
}

integrations/nginx/github.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package nginx
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"os"
9+
10+
"github.com/akitasoftware/akita-cli/printer"
11+
)
12+
13+
// The Github API asset type, with most fields missing
14+
type GithubAsset struct {
15+
Name string `json:"name"`
16+
ID int `json:"id"`
17+
}
18+
19+
// The Github API release type, with most fields missing
20+
type GithubRelease struct {
21+
TagName string `json:"tag_name"`
22+
Assets []GithubAsset `json:"assets"`
23+
}
24+
25+
// Return a list of all the assets available in the latest release
26+
func GetLatestReleaseAssets() ([]GithubAsset, error) {
27+
response, err := http.Get("https://api.github.com/repos/akitasoftware/akita-nginx-module/releases/latest")
28+
if err != nil {
29+
printer.Debugf("Error performing GET request: %v", err)
30+
return nil, err
31+
}
32+
defer response.Body.Close()
33+
34+
printer.Debugf("Status: %q\n", response.Status)
35+
if response.StatusCode != 200 {
36+
return nil, fmt.Errorf("Response code %d from Github", response.StatusCode)
37+
}
38+
39+
decoder := json.NewDecoder(response.Body)
40+
var release GithubRelease
41+
err = decoder.Decode(&release)
42+
if err != nil {
43+
printer.Debugf("JSON decode error: %v\n", err)
44+
return nil, err
45+
}
46+
47+
return release.Assets, nil
48+
}
49+
50+
// Download a specific asset (the prebuilt module) to a temporary file
51+
func DownloadReleaseAsset(id int, filename string) error {
52+
download, err := os.Create(filename)
53+
if err != nil {
54+
printer.Errorf("Can't create destination file: %v\n", err)
55+
return err
56+
}
57+
defer download.Close()
58+
59+
// Need to set a header to get the binary rather than JSON
60+
client := &http.Client{}
61+
url := fmt.Sprintf("https://api.github.com/repos/akitasoftware/akita-nginx-module/releases/assets/%d", id)
62+
req, err := http.NewRequest("GET", url, nil)
63+
if err != nil {
64+
printer.Errorf("Failed to create download request: %v\n", err)
65+
return err
66+
}
67+
68+
req.Header.Add("Accept", "application/octet-stream")
69+
response, err := client.Do(req)
70+
if err != nil {
71+
printer.Errorf("HTTP download failure: %v\n", err)
72+
return err
73+
}
74+
defer response.Body.Close()
75+
76+
if response.StatusCode != 200 {
77+
printer.Errorf("Module download has status %q\n", response.Status)
78+
return fmt.Errorf("Response code %d from Github", response.StatusCode)
79+
}
80+
81+
_, err = io.Copy(download, response.Body)
82+
if err != nil {
83+
printer.Errorf("HTTP download failure: %v\n", err)
84+
return err
85+
}
86+
87+
return nil
88+
}

0 commit comments

Comments
 (0)