318 lines
8.6 KiB
Go
318 lines
8.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.towk2.me/towk/makeshift/internal/archive"
|
|
"git.towk2.me/towk/makeshift/internal/kwargs"
|
|
"git.towk2.me/towk/makeshift/pkg/client"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
pluginArgs []string
|
|
pluginKWArgs kwargs.KWArgs = kwargs.KWArgs{}
|
|
)
|
|
var downloadCmd = cobra.Command{
|
|
Use: "download",
|
|
Example: `
|
|
# set up environment
|
|
export MAKESHIFT_HOST=http://localhost:5050
|
|
export MAKESHIFT_PATH=test
|
|
|
|
# download a file or directory (as archive)
|
|
makeshift download
|
|
makeshift download --host http://localhost:5050.com --path test
|
|
|
|
# download a file or directory and run plugins with profile data
|
|
makeshift download --plugins smd,jinja2 --profile compute
|
|
curl $MAKESHIFT_HOST/download/test?plugins=smd,jinja2&profile=test
|
|
|
|
# download directory and extract it's contents automatically
|
|
# then, remove the downloaded archive
|
|
makeshift download -xr
|
|
`,
|
|
Short: "Download and modify files with plugins",
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
|
setenv(cmd, "host", "MAKESHIFT_HOST")
|
|
setenv(cmd, "path", "MAKESHIFT_PATH")
|
|
setenv(cmd, "cacert", "MAKESHIFT_CACERT")
|
|
},
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
var (
|
|
host, _ = cmd.Flags().GetString("host")
|
|
path, _ = cmd.Flags().GetString("path")
|
|
outputPath, _ = cmd.Flags().GetString("output")
|
|
cacertPath, _ = cmd.Flags().GetString("cacert")
|
|
pluginNames, _ = cmd.Flags().GetStringSlice("plugins")
|
|
profileIDs, _ = cmd.Flags().GetStringSlice("profiles")
|
|
extract, _ = cmd.Flags().GetBool("extract")
|
|
removeArchive, _ = cmd.Flags().GetBool("remove-archive")
|
|
|
|
c = client.New(host)
|
|
res *http.Response
|
|
query string
|
|
body []byte
|
|
err error
|
|
)
|
|
|
|
query = fmt.Sprintf("/download/%s?", path)
|
|
if len(pluginNames) > 0 {
|
|
query += "plugins=" + url.QueryEscape(strings.Join(pluginNames, ","))
|
|
}
|
|
if len(profileIDs) > 0 {
|
|
query += "&profiles=" + url.QueryEscape(strings.Join(profileIDs, ","))
|
|
}
|
|
if len(pluginArgs) > 0 {
|
|
query += "&args=" + url.QueryEscape(strings.Join(pluginArgs, ","))
|
|
}
|
|
if len(pluginKWArgs) > 0 {
|
|
query += "&kwargs=" + url.QueryEscape(pluginKWArgs.String())
|
|
}
|
|
|
|
log.Debug().
|
|
Str("host", host).
|
|
Str("path", path).
|
|
Str("query", query).
|
|
Str("output", outputPath).
|
|
Strs("profiles", profileIDs).
|
|
Strs("plugins", pluginNames).
|
|
Send()
|
|
|
|
if cacertPath != "" {
|
|
c.LoadCertificateFromPath(cacertPath)
|
|
}
|
|
|
|
res, body, err = c.MakeRequest(client.HTTPEnvelope{
|
|
Path: query,
|
|
Method: http.MethodGet,
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).
|
|
Str("host", host).
|
|
Str("path", path).
|
|
Str("output", outputPath).
|
|
Msg("failed to make request")
|
|
os.Exit(1)
|
|
}
|
|
if res.StatusCode != http.StatusOK {
|
|
log.Error().
|
|
Any("status", map[string]any{
|
|
"code": res.StatusCode,
|
|
"message": res.Status,
|
|
}).
|
|
Str("host", host).
|
|
Str("path", path).
|
|
Str("output", outputPath).
|
|
Msg("response returned bad status")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// determine if output path is an archive or file
|
|
switch res.Header.Get("FILETYPE") {
|
|
case "archive":
|
|
// write archive to disk with or without '-o' specified
|
|
if outputPath == "" {
|
|
outputPath = fmt.Sprintf("%s.tar.gz", path)
|
|
writeFiles(outputPath, body)
|
|
log.Debug().Str("path", outputPath).Msg("wrote archive to pre-determined path")
|
|
} else {
|
|
writeFiles(outputPath, body)
|
|
log.Debug().Str("path", outputPath).Msg("wrote archive to specified path")
|
|
}
|
|
|
|
// extract files if '-x' flag is passed
|
|
if extract {
|
|
var (
|
|
dir = filepath.Dir(outputPath)
|
|
base = strings.TrimSuffix(filepath.Base(outputPath), ".tar.gz")
|
|
)
|
|
err = archive.Expand(outputPath, fmt.Sprintf("%s/%s", dir, base))
|
|
if err != nil {
|
|
log.Error().Err(err).
|
|
Str("path", outputPath).
|
|
Msg("failed to expand archive")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// optionally, remove archive if '-r' flag is passed
|
|
// NOTE: this can only be used if `-x` flag is set
|
|
if removeArchive {
|
|
if !extract {
|
|
log.Warn().Msg("requires '-x/--extract' flag to be set to 'true'")
|
|
} else {
|
|
err = os.Remove(outputPath)
|
|
if err != nil {
|
|
log.Error().Err(err).
|
|
Str("path", outputPath).
|
|
Msg("failed to remove archive")
|
|
}
|
|
}
|
|
}
|
|
case "file":
|
|
// write to file if '-o' specified otherwise stdout
|
|
if outputPath != "" {
|
|
writeFiles(outputPath, body)
|
|
log.Debug().Str("path", outputPath).Msg("wrote file to specified path")
|
|
} else {
|
|
fmt.Println(string(body))
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
var downloadProfileCmd = &cobra.Command{
|
|
Use: "profile",
|
|
Example: `
|
|
// download a profile
|
|
makeshift download profile default
|
|
`,
|
|
Args: cobra.ExactArgs(1),
|
|
Short: "Download a profile",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
var (
|
|
host, _ = cmd.Flags().GetString("host")
|
|
outputPath, _ = cmd.Flags().GetString("output")
|
|
cacertPath, _ = cmd.Flags().GetString("cacert")
|
|
|
|
c = client.New(host)
|
|
res *http.Response
|
|
body []byte
|
|
query string
|
|
err error
|
|
)
|
|
|
|
log.Debug().
|
|
Str("host", host).
|
|
Str("output", outputPath).
|
|
Send()
|
|
|
|
if cacertPath != "" {
|
|
c.LoadCertificateFromPath(cacertPath)
|
|
}
|
|
|
|
for _, profileID := range args {
|
|
query = fmt.Sprintf("/profiles/%s", profileID)
|
|
res, body, err = c.MakeRequest(client.HTTPEnvelope{
|
|
Path: query,
|
|
Method: http.MethodGet,
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).
|
|
Str("host", host).
|
|
Msg("failed to make request")
|
|
os.Exit(1)
|
|
}
|
|
if res.StatusCode != http.StatusOK {
|
|
log.Error().
|
|
Any("status", map[string]any{
|
|
"code": res.StatusCode,
|
|
"message": res.Status,
|
|
}).
|
|
Str("host", host).
|
|
Msg("response returned bad status")
|
|
os.Exit(1)
|
|
}
|
|
if outputPath != "" {
|
|
writeFiles(outputPath, body)
|
|
} else {
|
|
fmt.Println(string(body))
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
var downloadPluginCmd = &cobra.Command{
|
|
Use: "plugin",
|
|
Example: `
|
|
// download a plugin
|
|
makeshift download plugin smd jinja2
|
|
`,
|
|
Args: cobra.ExactArgs(1),
|
|
Short: "Download a raw plugin",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
var (
|
|
host, _ = cmd.Flags().GetString("host")
|
|
outputPath, _ = cmd.Flags().GetString("output")
|
|
cacertPath, _ = cmd.Flags().GetString("cacert")
|
|
|
|
c = client.New(host)
|
|
res *http.Response
|
|
query string
|
|
body []byte
|
|
err error
|
|
)
|
|
|
|
log.Debug().
|
|
Str("host", host).
|
|
Str("output", outputPath).
|
|
Send()
|
|
|
|
if cacertPath != "" {
|
|
c.LoadCertificateFromPath(cacertPath)
|
|
}
|
|
|
|
for _, pluginName := range args {
|
|
query = fmt.Sprintf("/plugins/%s/raw", pluginName)
|
|
res, body, err = c.MakeRequest(client.HTTPEnvelope{
|
|
Path: query,
|
|
Method: http.MethodGet,
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).
|
|
Str("host", host).
|
|
Str("query", query).
|
|
Msg("failed to make request")
|
|
os.Exit(1)
|
|
}
|
|
if res.StatusCode != http.StatusOK {
|
|
log.Error().
|
|
Any("status", map[string]any{
|
|
"code": res.StatusCode,
|
|
"message": res.Status,
|
|
}).
|
|
Str("host", host).
|
|
Msg("response returned bad status")
|
|
os.Exit(1)
|
|
}
|
|
if outputPath != "" {
|
|
writeFiles(outputPath, body)
|
|
} else {
|
|
writeFiles(fmt.Sprintf("%s.so", pluginName), body)
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
downloadCmd.PersistentFlags().String("host", "http://localhost:5050", "Set the makeshift remote host (can be set with MAKESHIFT_HOST)")
|
|
downloadCmd.PersistentFlags().StringP("output", "o", "", "Set the output path to write files")
|
|
downloadCmd.PersistentFlags().String("cacert", "", "Set the CA certificate path to load")
|
|
downloadCmd.Flags().StringP("path", "p", ".", "Set the path to list files (can be set with MAKESHIFT_PATH)")
|
|
downloadCmd.Flags().StringSlice("profiles", []string{}, "Set the profile(s) to use to populate data store")
|
|
downloadCmd.Flags().StringSlice("plugins", []string{}, "Set the plugin(s) to run before downloading files")
|
|
downloadCmd.Flags().StringSlice("plugin-args", []string{}, "Set the argument list to pass to plugin(s)")
|
|
downloadCmd.Flags().Var(&pluginKWArgs, "plugin-kwargs", "Set the argument map to pass to plugin(s)")
|
|
downloadCmd.Flags().BoolP("extract", "x", false, "Set whether to extract archive locally after downloading")
|
|
downloadCmd.Flags().BoolP("remove-archive", "r", false, "Set whether to remove the archive after extracting (used with '--extract' flag)")
|
|
|
|
downloadCmd.AddCommand(downloadProfileCmd, downloadPluginCmd)
|
|
|
|
rootCmd.AddCommand(&downloadCmd)
|
|
}
|
|
|
|
// helper to write downloaded files
|
|
func writeFiles(path string, body []byte) {
|
|
var err = os.WriteFile(path, body, 0o755)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("failed to write file(s) from download")
|
|
os.Exit(1)
|
|
}
|
|
}
|