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

Commit

Permalink
Install a precompiled module from the latest Github release. (#198)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
mgritter and liujed authored Mar 3, 2023
1 parent 417c021 commit 104fa0d
Show file tree
Hide file tree
Showing 4 changed files with 491 additions and 12 deletions.
49 changes: 37 additions & 12 deletions cmd/internal/nginx/nginx.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package nginx

import (
"fmt"

"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/akitasoftware/akita-cli/cmd/internal/pluginloader"
"github.com/akitasoftware/akita-cli/integrations/nginx"
"github.com/akitasoftware/akita-cli/printer"
"github.com/akitasoftware/akita-cli/rest"
"github.com/akitasoftware/akita-cli/telemetry"
)
Expand All @@ -19,16 +18,21 @@ var (
// Port number that the module will send traffic to
listenPortFlag uint16

// Dedvelopment mode -- dump out traffic locally
// Development mode -- dump out traffic locally
developmentFlag bool

// Dry run for install -- find version but do not install
dryRunFlag bool

// Module destination
moduleDestFlag string
)

var Cmd = &cobra.Command{
Use: "nginx",
Short: "Install or use Akita's NGINX module to collect API traffic.",
SilenceUsage: true,
// TODO: un-hide when ready for use
Hidden: true,
Hidden: false,
}

var CaptureCmd = &cobra.Command{
Expand All @@ -40,21 +44,23 @@ var CaptureCmd = &cobra.Command{
}

var InstallCmd = &cobra.Command{
// TODO: substitute in the real name
Use: "xinstall",
Use: "install",
Short: "Download a precompiled NGINX module.",
Long: "Download a precompiled version of akita-nginx-module that matches the currently installed version of NGINX.",
SilenceUsage: true,
RunE: installNginxModule,
}

func init() {
Cmd.PersistentFlags().StringVar(&projectFlag, "project", "", "Your Akita project.")
Cmd.PersistentFlags().Uint16Var(&listenPortFlag, "port", 50080, "The port number on which to listen for connections.")
Cmd.PersistentFlags().BoolVar(&developmentFlag, "dev", false, "Enable development mode; only dumps traffic.")
Cmd.PersistentFlags().MarkHidden("dev")
CaptureCmd.PersistentFlags().StringVar(&projectFlag, "project", "", "Your Akita project.")
CaptureCmd.PersistentFlags().Uint16Var(&listenPortFlag, "port", 50080, "The port number on which to listen for connections.")
CaptureCmd.PersistentFlags().BoolVar(&developmentFlag, "dev", false, "Enable development mode; only dumps traffic.")
CaptureCmd.PersistentFlags().MarkHidden("dev")

Cmd.AddCommand(CaptureCmd)

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

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

func installNginxModule(cmd *cobra.Command, args []string) error {
return fmt.Errorf("This command is not yet implemented.")
err := nginx.InstallModule(&nginx.InstallArgs{
DryRun: dryRunFlag,
})
if err != nil {
var installError *nginx.InstallationError
switch {
case errors.As(err, &installError):
// Log the error, then what the user should do next
printer.Errorf("%v\n", err)
printer.Infof("%v\n", installError.Remedy)
default:
printer.Errorf("Could not determine which NGINX platform and version to support: %v\n", err)
printer.Infof("Please contact [email protected] for assistance, or follow the instructions at https://github.com/akitasoftware/akita-nginx-module to install the module by hand.\n")
}

// Report the error here because we don't report it to the root command
telemetry.Error("command execution", err)

}
return nil
}
88 changes: 88 additions & 0 deletions integrations/nginx/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package nginx

import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"

"github.com/akitasoftware/akita-cli/printer"
)

// The Github API asset type, with most fields missing
type GithubAsset struct {
Name string `json:"name"`
ID int `json:"id"`
}

// The Github API release type, with most fields missing
type GithubRelease struct {
TagName string `json:"tag_name"`
Assets []GithubAsset `json:"assets"`
}

// Return a list of all the assets available in the latest release
func GetLatestReleaseAssets() ([]GithubAsset, error) {
response, err := http.Get("https://api.github.com/repos/akitasoftware/akita-nginx-module/releases/latest")
if err != nil {
printer.Debugf("Error performing GET request: %v", err)
return nil, err
}
defer response.Body.Close()

printer.Debugf("Status: %q\n", response.Status)
if response.StatusCode != 200 {
return nil, fmt.Errorf("Response code %d from Github", response.StatusCode)
}

decoder := json.NewDecoder(response.Body)
var release GithubRelease
err = decoder.Decode(&release)
if err != nil {
printer.Debugf("JSON decode error: %v\n", err)
return nil, err
}

return release.Assets, nil
}

// Download a specific asset (the prebuilt module) to a temporary file
func DownloadReleaseAsset(id int, filename string) error {
download, err := os.Create(filename)
if err != nil {
printer.Errorf("Can't create destination file: %v\n", err)
return err
}
defer download.Close()

// Need to set a header to get the binary rather than JSON
client := &http.Client{}
url := fmt.Sprintf("https://api.github.com/repos/akitasoftware/akita-nginx-module/releases/assets/%d", id)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
printer.Errorf("Failed to create download request: %v\n", err)
return err
}

req.Header.Add("Accept", "application/octet-stream")
response, err := client.Do(req)
if err != nil {
printer.Errorf("HTTP download failure: %v\n", err)
return err
}
defer response.Body.Close()

if response.StatusCode != 200 {
printer.Errorf("Module download has status %q\n", response.Status)
return fmt.Errorf("Response code %d from Github", response.StatusCode)
}

_, err = io.Copy(download, response.Body)
if err != nil {
printer.Errorf("HTTP download failure: %v\n", err)
return err
}

return nil
}
Loading

0 comments on commit 104fa0d

Please sign in to comment.