github.com/kristofferahl/go-centry@v1.5.0/cmd/centry/serve.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"os"
     8  	"strings"
     9  
    10  	api "github.com/kristofferahl/go-centry/internal/pkg/api"
    11  	"github.com/kristofferahl/go-centry/internal/pkg/config"
    12  	"github.com/kristofferahl/go-centry/internal/pkg/io"
    13  	"github.com/sirupsen/logrus"
    14  	"github.com/urfave/cli/v2"
    15  )
    16  
    17  // ServeCommand is a Command implementation that applies stuff
    18  type ServeCommand struct {
    19  	Manifest *config.Manifest
    20  	Log      *logrus.Entry
    21  }
    22  
    23  // ToCLICommand returns a CLI command
    24  func (sc *ServeCommand) ToCLICommand() *cli.Command {
    25  	return withCommandDefaults(&cli.Command{
    26  		Name:      "serve",
    27  		Usage:     "Exposes commands over HTTP",
    28  		UsageText: "",
    29  		Hidden:    false,
    30  		Action: func(c *cli.Context) error {
    31  			ec := sc.Run(c.Args().Slice())
    32  			if ec > 0 {
    33  				return cli.Exit("failed to start the server", ec)
    34  			}
    35  			return nil
    36  		},
    37  	})
    38  }
    39  
    40  // Run starts an HTTP server and blocks
    41  func (sc *ServeCommand) Run(args []string) int {
    42  	sc.Log.Debugf("serving HTTP api")
    43  
    44  	s := api.NewServer(api.Config{
    45  		Log:       sc.Log,
    46  		BasicAuth: configureBasicAuth(),
    47  	})
    48  
    49  	s.Router.HandleFunc("/", sc.indexHandler()).Methods("GET")
    50  	s.Router.HandleFunc("/commands/", sc.executeHandler()).Methods("POST")
    51  
    52  	err := s.RunAndBlock()
    53  	if err != nil {
    54  		return 1
    55  	}
    56  
    57  	return 0
    58  }
    59  
    60  func configureBasicAuth() *api.BasicAuth {
    61  	var auth *api.BasicAuth
    62  	baUsername := os.Getenv("CENTRY_SERVE_USERNAME")
    63  	baPassword := os.Getenv("CENTRY_SERVE_PASSWORD")
    64  
    65  	if baUsername != "" && baPassword != "" {
    66  		auth = &api.BasicAuth{
    67  			Username: baUsername,
    68  			Password: baPassword,
    69  		}
    70  	}
    71  
    72  	return auth
    73  }
    74  
    75  func (sc *ServeCommand) indexHandler() func(w http.ResponseWriter, r *http.Request) {
    76  	return func(w http.ResponseWriter, r *http.Request) {
    77  		statusCode := http.StatusOK
    78  		response := api.IndexResponse{}
    79  
    80  		w.Header().Set("Content-Type", "application/json")
    81  		w.WriteHeader(statusCode)
    82  
    83  		js, err := json.Marshal(response)
    84  		if err == nil {
    85  			w.Write(js)
    86  		}
    87  	}
    88  }
    89  
    90  func (sc *ServeCommand) executeHandler() func(w http.ResponseWriter, r *http.Request) {
    91  	return func(w http.ResponseWriter, r *http.Request) {
    92  		statusCode := http.StatusOK
    93  		response := api.ExecuteResponse{}
    94  
    95  		var body api.ExecuteRequest
    96  
    97  		decoder := json.NewDecoder(r.Body)
    98  		err := decoder.Decode(&body)
    99  		if err != nil {
   100  			statusCode = http.StatusBadRequest
   101  		}
   102  
   103  		args := []string{}
   104  		args = append(args, sc.Manifest.Path)
   105  		args = append(args, strings.Fields(body.Args)...)
   106  
   107  		// Build
   108  		io, buf := io.BufferedCombined()
   109  		context := NewContext(API, io)
   110  
   111  		context.commandEnabledFunc = func(cmd config.Command) bool {
   112  			serveAnnotation, _ := cmd.Annotation(config.CommandAnnotationAPINamespace, "serve")
   113  			if serveAnnotation == nil || serveAnnotation.Value != config.TrueString {
   114  				return false
   115  			}
   116  
   117  			return true
   118  		}
   119  
   120  		context.optionEnabledFunc = func(opt config.Option) bool {
   121  			serveAnnotation, _ := opt.Annotation(config.CommandAnnotationAPINamespace, "serve")
   122  			if serveAnnotation == nil || serveAnnotation.Value != config.TrueString {
   123  				return false
   124  			}
   125  
   126  			return true
   127  		}
   128  
   129  		runtime, err := NewRuntime(args, context)
   130  		if err != nil {
   131  			response.Centry = fmt.Sprintf("%s %s", context.manifest.Config.Name, context.manifest.Config.Version)
   132  			response.Result = fmt.Sprintf("Unable to create runtime %v", err)
   133  			response.ExitCode = 1
   134  			sc.Log.Error(response.Result)
   135  		} else {
   136  			// Run
   137  			exitCode := runtime.Execute()
   138  
   139  			response.Centry = fmt.Sprintf("%s %s", context.manifest.Config.Name, context.manifest.Config.Version)
   140  			response.Result = buf.String()
   141  			response.ExitCode = exitCode
   142  		}
   143  
   144  		w.Header().Set("Content-Type", "application/json")
   145  		w.WriteHeader(statusCode)
   146  
   147  		js, err := json.Marshal(response)
   148  		if err == nil {
   149  			w.Write(js)
   150  		}
   151  	}
   152  }