diff --git a/Makefile b/Makefile index 5e8d1f1..c1e7a87 100644 --- a/Makefile +++ b/Makefile @@ -36,13 +36,13 @@ container-testing: binaries plugins: $(plugin_binaries) # how to make each plugin -lib/%.so: pkg/plugins/%/*.go +lib/%.so: pkg/generator/plugins/%/*.go mkdir -p lib go build -buildmode=plugin -o $@ $< docs: - go doc git.towk2.me/towk/makeshift/cmd - go doc git.towk2.me/towk/makeshift/pkg/${prog} + go doc github.com/OpenCHAMI/cmd + go doc github.com/OpenCHAMI/pkg/${prog} # remove executable and all built plugins .PHONY: clean diff --git a/README.md b/README.md index 7bcfc54..dadab6f 100644 --- a/README.md +++ b/README.md @@ -209,5 +209,4 @@ There are some features still missing that will be added later. 5. Optionally build plugins directly into the main driver 6. Protected routes that require authentication 7. Configuration file for persistent runs -8. `Dockerfile` and `docker-compose.yml` files to build containers -9. Including certs with requests \ No newline at end of file +8. `Dockerfile` and `docker-compose.yml` files \ No newline at end of file diff --git a/bin/.gitkeep b/bin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bin/compile-plugins.sh b/bin/compile-plugins.sh deleted file mode 100644 index 278bc7c..0000000 --- a/bin/compile-plugins.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin bash - - -function compile_default_plugins() { - makeshift_exe=./makeshift - go_exe=go - - # make sure go build tools are installed - if command -v $go_exe >/dev/null 2>&1; then - # make sure that MAKESHIFT_ROOT is set - if [[ ! -v MAKESHIFT_ROOT ]]; then - # Compile the default external plugins - go build - $makeshift_exe compile pkg/plugins/jinja2/jinja2.go -o $MAKESHIFT_ROOT/plugins/jinja2.go - $makeshift_exe compile pkg/plugins/smd/smd.go -o $MAKESHIFT_ROOT/plugins/smd.so - $makeshift_exe compile pkg/plugins/userdata/userdata.go -o $MAKESHIFT_ROOT/plugins/userdata.go - else - echo "requires MAKESHIFT_ROOT to be set" - fi - else - echo "Go build tools must be installed" - fi -} - - -compile_default_plugins \ No newline at end of file diff --git a/cmd/delete.go b/cmd/delete.go deleted file mode 100644 index 85dbd71..0000000 --- a/cmd/delete.go +++ /dev/null @@ -1,123 +0,0 @@ -package cmd - -import ( - "fmt" - "net/http" - - "git.towk2.me/towk/makeshift/pkg/client" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" -) - -var deleteCmd = &cobra.Command{ - Use: "delete", - Example: ` - # set up environment - export MAKESHIFT_HOST=http://localhost:5050 - export MAKESHIFT_PATH=test - - # delete a file or directory (cannot delete root) - makeshift delete -p help.txt - makeshift delete --host http://localhost:5555 --path templates -`, - Short: "Delete files and directories", - PersistentPreRun: func(cmd *cobra.Command, args []string) { - setenv(cmd, "host", "MAKESHIFT_HOST") - setenv(cmd, "path", "MAKESHIFT_PATH") - }, - Run: func(cmd *cobra.Command, args []string) { - var ( - host, _ = cmd.Flags().GetString("host") - paths, _ = cmd.Flags().GetStringSlice("path") - - c = client.New(host) - res *http.Response - query string - err error - ) - for _, path := range paths { - if path == "" { - log.Warn().Msg("skipping empty path") - continue - } - - query = fmt.Sprintf("/delete/%s?", path) - res, _, err = c.MakeRequest(client.HTTPEnvelope{ - Path: query, - Method: http.MethodDelete, - }) - handleResponseError(res, host, query, err) - } - }, -} - -var deleteProfilesCmd = &cobra.Command{ - Use: "profiles", - Example: ` - # delete profile(s) by its ID - makeshift delete profiles kubernetes slurm compute -`, - Args: cobra.MinimumNArgs(1), - Short: "Delete profile(s)", - Run: func(cmd *cobra.Command, args []string) { - var ( - host, _ = cmd.Flags().GetString("host") - - c = client.New(host) - res *http.Response - query string - err error - ) - - for _, profileID := range args { - if profileID == "default" { - log.Warn().Msg("cannot delete the default profile") - continue - } - - query = fmt.Sprintf("/profiles/%s", profileID) - res, _, err = c.MakeRequest(client.HTTPEnvelope{ - Path: query, - Method: http.MethodDelete, - }) - handleResponseError(res, host, query, err) - } - }, -} - -var deletePluginsCmd = &cobra.Command{ - Use: "plugins", - Example: ` - # delete plugin(s) by name - makeshift delete plugins weather slurm user -`, - Args: cobra.MinimumNArgs(1), - Short: "Delete plugin(s)", - Run: func(cmd *cobra.Command, args []string) { - var ( - host, _ = cmd.Flags().GetString("host") - - c = client.New(host) - res *http.Response - query string - err error - ) - - for _, pluginName := range args { - query = fmt.Sprintf("/plugins/%s", pluginName) - res, _, err = c.MakeRequest(client.HTTPEnvelope{ - Path: query, - Method: http.MethodDelete, - }) - handleResponseError(res, host, query, err) - } - }, -} - -func init() { - deleteCmd.PersistentFlags().String("host", "http://localhost:5050", "Set the makeshift server host (can be set with MAKESHIFT_HOST)") - deleteCmd.Flags().StringSliceP("path", "p", []string{}, "Set the paths to delete files and directories") - deleteCmd.AddCommand(deleteProfilesCmd, deletePluginsCmd) - - rootCmd.AddCommand(deleteCmd) -} diff --git a/cmd/list.go b/cmd/list.go index 1fa3fe6..0de2797 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -81,10 +81,10 @@ var listPluginsCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { var ( host, _ = cmd.Flags().GetString("host") + path, _ = cmd.Flags().GetString("path") c = client.New(host) res *http.Response - query string plugins []string body []byte err error @@ -96,7 +96,24 @@ var listPluginsCmd = &cobra.Command{ Path: "/plugins", Method: http.MethodGet, }) - handleResponseError(res, host, "/plugins", err) + if err != nil { + log.Error().Err(err). + Str("host", host). + Str("path", path). + 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). + Msg("response returned bad status") + os.Exit(1) + } err = json.Unmarshal(body, &plugins) if err != nil { log.Error().Err(err). @@ -106,12 +123,28 @@ var listPluginsCmd = &cobra.Command{ } else { for _, pluginName := range args { // make request to /list endpoint - query = fmt.Sprintf("/plugins/%s/info", pluginName) res, body, err = c.MakeRequest(client.HTTPEnvelope{ - Path: query, + Path: fmt.Sprintf("/plugins/%s/info", pluginName), Method: http.MethodGet, }) - handleResponseError(res, host, query, err) + if err != nil { + log.Error().Err(err). + Str("host", host). + Str("path", path). + 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). + Msg("response returned bad status") + os.Exit(1) + } plugins = append(plugins, string(body)) } @@ -126,12 +159,12 @@ var listProfilesCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { var ( host, _ = cmd.Flags().GetString("host") + path, _ = cmd.Flags().GetString("path") c = client.New(host) res *http.Response profiles []makeshift.Profile body []byte - query string err error ) @@ -141,7 +174,24 @@ var listProfilesCmd = &cobra.Command{ Path: "/profiles", Method: http.MethodGet, }) - handleResponseError(res, host, "/profiles", err) + if err != nil { + log.Error().Err(err). + Str("host", host). + Str("path", path). + 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). + Msg("response returned bad status") + os.Exit(1) + } err = json.Unmarshal(body, &profiles) if err != nil { @@ -152,12 +202,28 @@ var listProfilesCmd = &cobra.Command{ } else { for _, profileID := range args { // make request to /list endpoint - query = fmt.Sprintf("/profiles/%s", profileID) res, body, err = c.MakeRequest(client.HTTPEnvelope{ - Path: fmt.Sprintf(query), + Path: fmt.Sprintf("/profiles/%s", profileID), Method: http.MethodGet, }) - handleResponseError(res, host, query, err) + if err != nil { + log.Error().Err(err). + Str("host", host). + Str("path", path). + 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). + Msg("response returned bad status") + os.Exit(1) + } var profile makeshift.Profile err = json.Unmarshal(body, &profile) if err != nil { diff --git a/cmd/plugins.go b/cmd/plugins.go index 8917b1c..812b5a8 100644 --- a/cmd/plugins.go +++ b/cmd/plugins.go @@ -178,8 +178,8 @@ var pluginsInfoCmd = &cobra.Command{ } func init() { - pluginsCompileCmd.Flags().StringP("output", "o", "", "Set the path to save compiled plugin (matches source type, i.e. uses files or directory)") - pluginsInfoCmd.Flags().String("host", "http://localhost:5050", "Set the makeshift remote host (can be set with MAKESHIFT_HOST)") + pluginsCompileCmd.PersistentFlags().StringP("output", "o", "", "Set the path to save compiled plugin") + pluginsInfoCmd.PersistentFlags().String("host", "http://localhost:5050", "Set the makeshift remote host (can be set with MAKESHIFT_HOST)") pluginsCmd.AddCommand(pluginsCompileCmd, pluginsInspectCmd, pluginsInfoCmd) rootCmd.AddCommand(pluginsCmd) diff --git a/cmd/root.go b/cmd/root.go index fb354f0..caa383c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "net/http" "os" logger "git.towk2.me/towk/makeshift/pkg/log" @@ -79,16 +78,6 @@ func setenv(cmd *cobra.Command, varname string, envvar string) { } } -func setenvp(cmd *cobra.Command, varname string, envvar string) { - if cmd.Flags().Changed(varname) { - return - } - val := os.Getenv(envvar) - if val != "" { - cmd.PersistentFlags().Set(varname, val) - } -} - func initLogger() { // initialize the logger logfile, _ := rootCmd.PersistentFlags().GetString("log-file") @@ -98,23 +87,3 @@ func initLogger() { os.Exit(1) } } - -func handleResponseError(res *http.Response, host, query string, err error) { - 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) - } -} diff --git a/cmd/run.go b/cmd/run.go index ab698e7..399aa83 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -5,16 +5,14 @@ import "github.com/spf13/cobra" var runCmd = &cobra.Command{ Use: "run", Example: ` - NOTE: This command is not implemented yet! - # set up environment export MAKESHIFT_HOST=http://localhost:5050 export MAKESHIFT_PATH=help.txt - export MAKESHIFT_ROOT=/opt/makeshift + export MAKESHIFT_ROOT=./test # run locally similar to 'download' makeshift run --plugins jinja2 --profiles default - makeshift run --root $HOME/apps/makeshift -p help.txt --plugins jinja2 --profiles default + makeshift run --root ./test -p help.txt --plugins jinja2 --profiles default `, Args: cobra.NoArgs, Short: "Run locally with plugins and profiles", diff --git a/cmd/upload.go b/cmd/upload.go index ecdd638..12b58b1 100644 --- a/cmd/upload.go +++ b/cmd/upload.go @@ -4,127 +4,72 @@ import ( "bufio" "encoding/json" "fmt" - "net/http" "os" "strings" "git.towk2.me/towk/makeshift/internal/format" - makeshift "git.towk2.me/towk/makeshift/pkg" - "git.towk2.me/towk/makeshift/pkg/client" "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) var ( inputFormat format.DataFormat = format.JSON + dataArgs []string ) var uploadCmd = &cobra.Command{ Use: "upload", Example: ` - # upload a single file in root directory - makeshift upload -d @compute-base.yaml + # upload a single file + makeshift upload -d @compute-base.yaml -t file + + # upload a single file with contents without specify type + makeshift upload -d '{"name": "John Smith", "email": "john.smith@example.com"}' # upload a directory - makeshift upload -d @setup/ + makeshift upload -d @setup/ -t directory # upload an archive (extracted and saved on server) makeshift upload -d @setup.tar.gz -t archive - - # upload a new profile with a specific path (used to set remote location) - makeshift upload profile -d @kubernetes.json -p nodes/kubernetes.json - makeshift upload profile -d @slurm.json -@compute.json -p nodes `, - Short: "Upload files and directories", - PersistentPreRun: func(cmd *cobra.Command, args []string) { - setenv(cmd, "host", "MAKESHIFT_HOST") - setenv(cmd, "path", "MAKESHIFT_PATH") - }, Run: func(cmd *cobra.Command, args []string) { - var ( - dataArgs, _ = cmd.Flags().GetStringArray("data") - - inputData = processFiles(dataArgs) - useDirectoryPath = len(inputData) > 1 - ) - for path, contents := range inputData { - log.Info().Str("path", path).Int("size", len(contents)).Send() - if useDirectoryPath { - - } else { - + // make one request be host positional argument (restricted to 1 for now) + var inputData []map[string]any + temp := append(handleArgs(args), processDataArgs(dataArgs)...) + for _, data := range temp { + if data != nil { + inputData = append(inputData, data) } } }, } -var uploadProfilesCmd = &cobra.Command{ - Use: "profile [profileID]", +var uploadProfileCmd = &cobra.Command{ + Use: "profile", Example: ` # upload a new profile makeshift upload profile -d @compute.json - # upload a new profile with a specific path (used for lookup) + # upload a new profile with a specific name (used for lookups) makeshift upload profile -d @kubernetes.json -n k8s `, - Args: cobra.NoArgs, + Args: cobra.ExactArgs(1), Short: "Upload a new profile", Run: func(cmd *cobra.Command, args []string) { // make one request be host positional argument (restricted to 1 for now) - var ( - // inputData []map[string]any = append(handleArgs(args), processDataArgs(dataArgs)...) - host, _ = cmd.Flags().GetString("host") - dataArgs, _ = cmd.Flags().GetStringArray("data") - profiles = processProfiles(dataArgs) - - c = client.New(host) - res *http.Response - query string - body []byte - err error - ) - - for _, profile := range profiles { - if profile == nil { - continue - } - - body, err = json.Marshal(profile) - if err != nil { - log.Error().Err(err).Msg("failed to marshal profile") - continue - } - - // send data to server - query = fmt.Sprintf("/profiles/%s", profile.ID) - res, body, err = c.MakeRequest(client.HTTPEnvelope{ - Path: query, - Method: http.MethodPost, - Body: body, - }) - 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) + var inputData []map[string]any + temp := append(handleArgs(args), processDataArgs(dataArgs)...) + for _, data := range temp { + if data != nil { + inputData = append(inputData, data) } } + }, } -var uploadPluginsCmd = &cobra.Command{ +var uploadPluginCmd = &cobra.Command{ Use: "plugin", Example: ` # upload a new plugin @@ -137,12 +82,8 @@ var uploadPluginsCmd = &cobra.Command{ Short: "Upload a new plugin", Run: func(cmd *cobra.Command, args []string) { // make one request be host positional argument (restricted to 1 for now) - // temp := append(handleArgs(args), processDataArgs(dataArgs)...) - var ( - inputData []*makeshift.Profile - dataArgs, _ = cmd.PersistentFlags().GetStringArray("data") - ) - temp := processProfiles(dataArgs) + var inputData []map[string]any + temp := append(handleArgs(args), processDataArgs(dataArgs)...) for _, data := range temp { if data != nil { inputData = append(inputData, data) @@ -152,62 +93,27 @@ var uploadPluginsCmd = &cobra.Command{ } func init() { - uploadCmd.PersistentFlags().String("host", "http://localhost:5050", "Set the makeshift remote host (can be set with MAKESHIFT_HOST)") - uploadCmd.PersistentFlags().StringArrayP("data", "d", []string{}, "Set the data to send to specified host (prepend @ for files)") - uploadCmd.Flags().StringP("path", "p", ".", "Set the path to list files (can be set with MAKESHIFT_PATH)") + uploadProfileCmd.Flags().VarP(&inputFormat, "format", "F", "Set the input format for profile") - uploadProfilesCmd.Flags().VarP(&inputFormat, "format", "F", "Set the input format for profile") - - uploadCmd.AddCommand(uploadProfilesCmd, uploadPluginsCmd) + uploadCmd.AddCommand(uploadProfileCmd, uploadPluginCmd) rootCmd.AddCommand(uploadCmd) } -func processFiles(args []string) map[string][]byte { - // load data either from file or directly from args - var collection = make(map[string][]byte, len(args)) - for _, arg := range args { - // if arg is empty string, then skip and continue - if len(arg) > 0 { - // determine if we're reading from file to load contents - if strings.HasPrefix(arg, "@") { - var ( - path string = strings.TrimLeft(arg, "@") - contents []byte - err error - ) - contents, err = os.ReadFile(path) - if err != nil { - log.Error().Err(err).Str("path", path).Msg("failed to read file") - continue - } - - // skip empty files - if len(contents) == 0 { - log.Warn().Str("path", path).Msg("file is empty") - continue - } - - // add loaded data to collection of all data - collection[path] = contents - } else { - log.Warn().Msg("only files can be uploaded") - - continue - } - } - } - return collection -} - -// processProfiles takes a slice of strings that check for the @ symbol and loads +// processDataArgs takes a slice of strings that check for the @ symbol and loads // the contents from the file specified in place (which replaces the path). // // NOTE: The purpose is to make the input arguments uniform for our request. This // function is meant to handle data passed with the `-d/--data` flag and positional // args from the CLI. -func processProfiles(args []string) []*makeshift.Profile { +func processDataArgs(args []string) []map[string]any { + // JSON representation + type ( + JSONObject = map[string]any + JSONArray = []JSONObject + ) + // load data either from file or directly from args - var collection = make([]*makeshift.Profile, len(args)) + var collection = make(JSONArray, len(args)) for i, arg := range args { // if arg is empty string, then skip and continue if len(arg) > 0 { @@ -216,7 +122,7 @@ func processProfiles(args []string) []*makeshift.Profile { var ( path string = strings.TrimLeft(arg, "@") contents []byte - data *makeshift.Profile + data JSONArray err error ) contents, err = os.ReadFile(path) @@ -232,17 +138,17 @@ func processProfiles(args []string) []*makeshift.Profile { } // convert/validate input data - data, err = parseProfile(contents, format.DataFormatFromFileExt(path, inputFormat)) + data, err = parseInput(contents, format.DataFormatFromFileExt(path, inputFormat)) if err != nil { log.Error().Err(err).Str("path", path).Msg("failed to validate input from file") } // add loaded data to collection of all data - collection = append(collection, data) + collection = append(collection, data...) } else { // input should be a valid JSON var ( - data *makeshift.Profile + data JSONArray input = []byte(arg) err error ) @@ -254,23 +160,57 @@ func processProfiles(args []string) []*makeshift.Profile { if err != nil { log.Error().Err(err).Msgf("failed to unmarshal input for argument %d", i) } - return []*makeshift.Profile{data} + return data } } } return collection } -func parseProfile(contents []byte, dataFormat format.DataFormat) (*makeshift.Profile, error) { +func handleArgs(args []string) []map[string]any { + // JSON representation + type ( + JSONObject = map[string]any + JSONArray = []JSONObject + ) + // no file to load, so we just use the joined args (since each one is a new line) + // and then stop var ( - data *makeshift.Profile + collection JSONArray + data []byte + err error + ) + + if len(dataArgs) > 0 { + return nil + } + data, err = ReadStdin() + if err != nil { + log.Error().Err(err).Msg("failed to read from standard input") + return nil + } + if len(data) == 0 { + log.Warn().Msg("no data found from standard input") + return nil + } + fmt.Println(string(data)) + collection, err = parseInput([]byte(data), inputFormat) + if err != nil { + log.Error().Err(err).Msg("failed to validate input from arg") + } + return collection +} + +func parseInput(contents []byte, dataFormat format.DataFormat) ([]map[string]any, error) { + var ( + data []map[string]any err error ) // convert/validate JSON input format err = format.Unmarshal(contents, &data, dataFormat) if err != nil { - return nil, fmt.Errorf("failed to unmarshal profile: %v", err) + return nil, fmt.Errorf("failed to unmarshal data: %v", err) } return data, nil } diff --git a/pkg/client/client.go b/pkg/client/client.go index 028fced..f9cf5d9 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -1,16 +1,12 @@ package client import ( - "crypto/tls" - "crypto/x509" "fmt" "io" "mime/multipart" - "net" "net/http" "os" "strings" - "time" "git.towk2.me/towk/makeshift/pkg/util" "github.com/cavaliergopher/grab/v3" @@ -111,43 +107,6 @@ func (c *Client) UploadMultipartFile(uri, key, path string) (*http.Response, err return resp, nil } -func (c *Client) LoadCertificateFromPath(path string) error { - cacert, err := os.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read certificate at path: %s", path) - } - certPool := x509.NewCertPool() - certPool.AppendCertsFromPEM(cacert) - err = c.LoadCertificateFromPool(certPool) - if err != nil { - return fmt.Errorf("could not initialize certificate from pool: %v", err) - } - return nil -} - -func (c *Client) LoadCertificateFromPool(certPool *x509.CertPool) error { - // make sure we have a valid cert pool - if certPool == nil { - return fmt.Errorf("invalid cert pool") - } - - // make sure that we can access the internal client - c.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{ - RootCAs: certPool, - InsecureSkipVerify: false, - }, - DisableKeepAlives: true, - Dial: (&net.Dialer{ - Timeout: 120 * time.Second, - KeepAlive: 120 * time.Second, - }).Dial, - TLSHandshakeTimeout: 120 * time.Second, - ResponseHeaderTimeout: 120 * time.Second, - } - return nil -} - func mustOpen(f string) *os.File { r, err := os.Open(f) if err != nil { diff --git a/pkg/plugins/mapper/mapper.go b/pkg/plugins/mapper/mapper.go deleted file mode 100644 index 1fded1f..0000000 --- a/pkg/plugins/mapper/mapper.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import "git.towk2.me/towk/makeshift/pkg/storage" - -type Mapper struct{} - -func (p *Mapper) Name() string { return "jinja2" } -func (p *Mapper) Version() string { return "test" } -func (p *Mapper) Description() string { return "Renders Jinja 2 templates" } -func (p *Mapper) Metadata() map[string]string { - return map[string]string{ - "author.name": "David J. Allen", - "author.email": "davidallendj@gmail.com", - } -} - -func (p *Mapper) Init() error { - // nothing to initialize - return nil -} - -func (p *Mapper) Run(data storage.KVStore, args []string) error { - return nil -} - -func (p *Mapper) Clean() error { - return nil -} - -var Makeshift Mapper diff --git a/pkg/plugins/userdata/userdata.go b/pkg/plugins/userdata/userdata.go new file mode 100644 index 0000000..ae5d31f --- /dev/null +++ b/pkg/plugins/userdata/userdata.go @@ -0,0 +1,30 @@ +package main + +import "git.towk2.me/towk/makeshift/pkg/storage" + +type UserData struct{} + +func (p *UserData) Name() string { return "jinja2" } +func (p *UserData) Version() string { return "test" } +func (p *UserData) Description() string { return "Renders Jinja 2 templates" } +func (p *UserData) Metadata() map[string]string { + return map[string]string{ + "author.name": "David J. Allen", + "author.email": "davidallendj@gmail.com", + } +} + +func (p *UserData) Init() error { + // nothing to initialize + return nil +} + +func (p *UserData) Run(data storage.KVStore, args []string) error { + return nil +} + +func (p *UserData) Clean() error { + return nil +} + +var Makeshift UserData diff --git a/pkg/service/plugins.go b/pkg/service/plugins.go index 0ab12eb..0dd516e 100644 --- a/pkg/service/plugins.go +++ b/pkg/service/plugins.go @@ -8,7 +8,6 @@ import ( makeshift "git.towk2.me/towk/makeshift/pkg" "github.com/go-chi/chi/v5" - "github.com/rs/zerolog/log" ) func (s *Service) ListPlugins() http.HandlerFunc { @@ -125,17 +124,24 @@ func (s *Service) CreatePlugin() http.HandlerFunc { func (s *Service) DeletePlugin() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( - pluginName = chi.URLParam(r, "name") - path = s.PathForPluginWithName(pluginName) - err error + path string + plugin makeshift.Plugin + err error ) - log.Debug().Str("path", path).Send() + plugin, err = getPluginFromRequestBody(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + path = s.PathForPluginWithName(plugin.Name()) err = os.Remove(path) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + w.WriteHeader(http.StatusOK) } } diff --git a/pkg/service/profiles.go b/pkg/service/profiles.go index e951e26..edef023 100644 --- a/pkg/service/profiles.go +++ b/pkg/service/profiles.go @@ -88,10 +88,13 @@ func (s *Service) GetProfile() http.HandlerFunc { func (s *Service) CreateProfile() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + type input struct { + Path string `json:"path"` + Profile *makeshift.Profile `json:"profile"` + } var ( body, contents []byte - path string - profile *makeshift.Profile + in input err error ) @@ -102,24 +105,23 @@ func (s *Service) CreateProfile() http.HandlerFunc { } // use the request info to build profile - err = json.Unmarshal(body, &profile) + err = json.Unmarshal(body, &in) if err != nil { - http.Error(w, fmt.Sprintf("failed to unmarshal profile: %v", err.Error()), http.StatusBadRequest) + http.Error(w, err.Error(), http.StatusBadRequest) return } // serialize just the profile part - contents, err = json.Marshal(profile) + contents, err = json.Marshal(in.Profile) if err != nil { - http.Error(w, fmt.Sprintf("failed to marshal profile: %v", err.Error()), http.StatusBadRequest) + http.Error(w, err.Error(), http.StatusBadRequest) return } // create a new profile on disk - path = s.PathForProfileWithID(profile.ID) - err = os.WriteFile(path, contents, os.ModePerm) + err = os.WriteFile(in.Path, contents, os.ModePerm) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -127,29 +129,6 @@ func (s *Service) CreateProfile() http.HandlerFunc { } } -func (s *Service) DeleteProfile() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - profileID = chi.URLParam(r, "id") - path string - err error - ) - - if profileID == "default" { - http.Error(w, "cannot delete the default profile", http.StatusBadRequest) - return - } - - path = s.PathForProfileWithID(profileID) - err = os.Remove(path) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusOK) - } -} - func (s *Service) SetProfileData() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var ( diff --git a/pkg/service/routes.go b/pkg/service/routes.go index 23d2d05..42e7338 100644 --- a/pkg/service/routes.go +++ b/pkg/service/routes.go @@ -181,9 +181,18 @@ func (s *Service) Download() http.HandlerFunc { func (s *Service) Upload() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var ( - _ = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/upload") - ) + + } +} + +func (s *Service) UploadPlugin() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + } +} + +func (s *Service) UploadProfile() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { } } @@ -230,23 +239,6 @@ func (s *Service) List() http.HandlerFunc { } } -func (s *Service) Delete() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var ( - path = s.PathForData() + strings.TrimPrefix(r.URL.Path, "/delete") - err error - ) - - err = os.RemoveAll(path) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - } -} - func (s *Service) GetStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(map[string]any{ diff --git a/pkg/service/service.go b/pkg/service/service.go index 362a08c..b75bd57 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -98,15 +98,15 @@ func (s *Service) Serve() error { } else { // general router.Get("/download/*", s.Download()) - router.Post("/upload/*", s.Upload()) + router.Post("/upload/", s.Upload()) + router.Post("/upload/plugin", s.UploadPlugin()) + router.Post("/upload/profile", s.UploadProfile()) router.Get("/list/*", s.List()) - router.Delete("/delete/*", s.Delete()) // profiles router.Get("/profiles", s.ListProfiles()) router.Get("/profiles/{id}", s.GetProfile()) router.Post("/profiles/{id}", s.CreateProfile()) - router.Delete("/profiles/{id}", s.DeleteProfile()) router.Get("/profiles/{id}/data", s.GetProfileData()) router.Post("/profiles/{id}/data", s.SetProfileData()) router.Delete("/profiles/{id}/data", s.DeleteProfileData())