From 0f8626ab1a17019d1c5f47cd2bb27d39146dc1c4 Mon Sep 17 00:00:00 2001 From: "Frederick F. Kautz IV" Date: Wed, 22 Apr 2015 21:31:29 -0700 Subject: [PATCH] Refactoring cli to take commands --- main.go | 229 +++++++++++++++++++++++++++++++++++-------- pkg/server/server.go | 180 ---------------------------------- 2 files changed, 189 insertions(+), 220 deletions(-) diff --git a/main.go b/main.go index 38efe7d27..cd78d07ce 100644 --- a/main.go +++ b/main.go @@ -25,14 +25,99 @@ import ( "strings" "time" + "errors" "github.com/minio-io/cli" + "github.com/minio-io/minio/pkg/api" + "github.com/minio-io/minio/pkg/api/web" "github.com/minio-io/minio/pkg/iodine" "github.com/minio-io/minio/pkg/server" + "github.com/minio-io/minio/pkg/server/httpserver" + "github.com/minio-io/minio/pkg/storage/drivers/memory" "github.com/minio-io/minio/pkg/utils/log" + "reflect" ) var globalDebugFlag = false +var commands = []cli.Command{ + modeCmd, +} + +var modeCommands = []cli.Command{ + memoryCmd, + donutCmd, +} + +var modeCmd = cli.Command{ + Name: "mode", + Subcommands: modeCommands, +} + +var memoryCmd = cli.Command{ + Name: "memory", + Action: runMemory, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "max-memory", + Value: "100M", + Usage: "", + }, + }, +} + +var donutCmd = cli.Command{ + Name: "donut", + Action: runDonut, + Flags: []cli.Flag{}, +} + +type memoryFactory struct { + server.Config + + maxMemory int64 +} + +func (f memoryFactory) getStartServerFunc() startServerFunc { + return func() (chan<- string, <-chan error) { + httpConfig := httpserver.Config{} + httpConfig.Address = f.Address + httpConfig.TLS = f.TLS + httpConfig.CertFile = f.CertFile + httpConfig.KeyFile = f.KeyFile + httpConfig.Websocket = false + _, _, driver := memory.Start(1024 * 1024 * 1024) + ctrl, status, _ := httpserver.Start(api.HTTPHandler(f.Domain, driver), httpConfig) + return ctrl, status + } +} + +type webFactory struct { + server.Config +} + +func (f webFactory) getStartServerFunc() startServerFunc { + return func() (chan<- string, <-chan error) { + httpConfig := httpserver.Config{} + httpConfig.Address = f.Address + httpConfig.TLS = f.TLS + httpConfig.CertFile = f.CertFile + httpConfig.KeyFile = f.KeyFile + httpConfig.Websocket = false + ctrl, status, _ := httpserver.Start(web.HTTPHandler(), httpConfig) + return ctrl, status + } +} + +type donutFactory struct { + server.Config +} + +func (f donutFactory) getStartServerFunc() startServerFunc { + return func() (chan<- string, <-chan error) { + return nil, nil + } +} + var flags = []cli.Flag{ cli.StringFlag{ Name: "domain,d", @@ -61,11 +146,6 @@ var flags = []cli.Flag{ Value: "", Usage: "key.pem", }, - cli.StringFlag{ - Name: "driver-type,t", - Value: "donut", - Usage: "valid entries: file,inmemory,donut", - }, cli.BoolFlag{ Name: "debug", Usage: "print debug information", @@ -80,58 +160,126 @@ func init() { } } -func getDriverType(input string) server.DriverType { - switch { - case input == "memory": - return server.Memory - case input == "donut": - return server.Donut - case input == "": - return server.Donut - default: - { - log.Fatal("Unknown driver type: '", input, "', Please specify a valid driver.") - return -1 // should never reach here - } +type startServerFunc func() (chan<- string, <-chan error) + +func runCmd(c *cli.Context) { + // default to memory driver, 1GB + apiServerConfig := getAPIServerConfig(c) + memoryDriver := memoryFactory{ + Config: apiServerConfig, + maxMemory: 1024 * 1024 * 1024, } + apiServer := memoryDriver.getStartServerFunc() + webServer := getWebServerConfigFunc(c) + servers := []startServerFunc{apiServer, webServer} + startMinio(servers) } -func runCmd(c *cli.Context) { - driverTypeStr := c.String("driver-type") - domain := c.String("domain") - apiaddress := c.String("api-address") - webaddress := c.String("web-address") +func runMemory(c *cli.Context) { + apiServerConfig := getAPIServerConfig(c) + // TODO max-memory should take human readable values + maxMemory, err := strconv.ParseInt(c.String("max-memory"), 10, 64) + if err != nil { + log.Println("max-memory not a numeric value") + } + memoryDriver := memoryFactory{ + Config: apiServerConfig, + maxMemory: maxMemory, + } + apiServer := memoryDriver.getStartServerFunc() + webServer := getWebServerConfigFunc(c) + servers := []startServerFunc{apiServer, webServer} + startMinio(servers) +} + +func runDonut(c *cli.Context) { + apiServerConfig := getAPIServerConfig(c) + donutDriver := donutFactory{ + Config: apiServerConfig, + } + apiServer := donutDriver.getStartServerFunc() + webServer := getWebServerConfigFunc(c) + servers := []startServerFunc{apiServer, webServer} + startMinio(servers) +} + +func getAPIServerConfig(c *cli.Context) server.Config { certFile := c.String("cert") keyFile := c.String("key") if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") { log.Fatal("Both certificate and key must be provided to enable https") } tls := (certFile != "" && keyFile != "") - driverType := getDriverType(driverTypeStr) - var serverConfigs []server.Config - apiServerConfig := server.Config{ - Domain: domain, - Address: apiaddress, + return server.Config{ + Domain: c.String("domain"), + Address: c.String("api-address"), TLS: tls, CertFile: certFile, KeyFile: keyFile, - APIType: server.MinioAPI{ - DriverType: driverType, - }, } - webUIServerConfig := server.Config{ - Domain: domain, - Address: webaddress, +} + +func getWebServerConfigFunc(c *cli.Context) startServerFunc { + config := server.Config{ + Domain: c.String("domain"), + Address: c.String("web-address"), TLS: false, CertFile: "", KeyFile: "", - APIType: server.Web{ - Websocket: false, - }, } - serverConfigs = append(serverConfigs, apiServerConfig) - serverConfigs = append(serverConfigs, webUIServerConfig) - server.Start(serverConfigs) + webDrivers := webFactory{ + Config: config, + } + return webDrivers.getStartServerFunc() +} + +func startMinio(servers []startServerFunc) { + var ctrlChannels []chan<- string + var errChannels []<-chan error + for _, server := range servers { + ctrlChannel, errChannel := server() + ctrlChannels = append(ctrlChannels, ctrlChannel) + errChannels = append(errChannels, errChannel) + } + cases := createSelectCases(errChannels) + for len(cases) > 0 { + chosen, value, recvOk := reflect.Select(cases) + switch recvOk { + case true: + // Status Message Received + switch true { + case value.Interface() != nil: + // For any error received cleanup all existing channels and fail + for _, ch := range ctrlChannels { + close(ch) + } + msg := fmt.Sprintf("%q", value.Interface()) + log.Fatal(iodine.New(errors.New(msg), nil)) + } + case false: + // Channel closed, remove from list + var aliveStatusChans []<-chan error + for i, ch := range errChannels { + if i != chosen { + aliveStatusChans = append(aliveStatusChans, ch) + } + } + // create new select cases without defunct channel + errChannels = aliveStatusChans + cases = createSelectCases(errChannels) + } + } +} + +func createSelectCases(channels []<-chan error) []reflect.SelectCase { + cases := make([]reflect.SelectCase, len(channels)) + for i, ch := range channels { + cases[i] = reflect.SelectCase{ + Dir: reflect.SelectRecv, + Chan: reflect.ValueOf(ch), + } + } + return cases } // Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B @@ -191,6 +339,7 @@ func main() { app.Usage = "Minimalist Object Storage" app.EnableBashCompletion = true app.Flags = flags + app.Commands = commands app.Action = runCmd app.Before = func(c *cli.Context) error { globalDebugFlag = c.GlobalBool("debug") diff --git a/pkg/server/server.go b/pkg/server/server.go index bd9fea374..5680aff67 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -16,24 +16,6 @@ package server -import ( - "fmt" - "os/user" - "path" - "reflect" - - "errors" - - "github.com/minio-io/minio/pkg/api" - "github.com/minio-io/minio/pkg/api/web" - "github.com/minio-io/minio/pkg/iodine" - "github.com/minio-io/minio/pkg/server/httpserver" - "github.com/minio-io/minio/pkg/storage/drivers" - "github.com/minio-io/minio/pkg/storage/drivers/donut" - "github.com/minio-io/minio/pkg/storage/drivers/memory" - "github.com/minio-io/minio/pkg/utils/log" -) - // Config - http server parameters type Config struct { Domain string @@ -43,165 +25,3 @@ type Config struct { KeyFile string APIType interface{} } - -// MinioAPI - driver type donut, file, memory -type MinioAPI struct { - DriverType DriverType -} - -// Web - web related -type Web struct { - Websocket bool // TODO -} - -// DriverType - different driver types supported by minio -type DriverType int - -// Driver types -const ( - Memory DriverType = iota - Donut -) - -func getHTTPChannels(configs []Config) (ctrlChans []chan<- string, statusChans []<-chan error) { - // a pair of control channels, we use these primarily to add to the lists above - var ctrlChan chan<- string - var statusChan <-chan error - - for _, config := range configs { - switch k := config.APIType.(type) { - case MinioAPI: - { - // configure web server - var driver drivers.Driver - var httpConfig = httpserver.Config{} - httpConfig.Address = config.Address - httpConfig.Websocket = false - httpConfig.TLS = config.TLS - - if config.CertFile != "" { - httpConfig.CertFile = config.CertFile - } - if config.KeyFile != "" { - httpConfig.KeyFile = config.KeyFile - } - - ctrlChans, statusChans, driver = getDriverChannels(k.DriverType) - // start minio api in a web server, pass driver driver into it - ctrlChan, statusChan, _ = httpserver.Start(api.HTTPHandler(config.Domain, driver), httpConfig) - - ctrlChans = append(ctrlChans, ctrlChan) - statusChans = append(statusChans, statusChan) - - } - case Web: - { - var httpConfig = httpserver.Config{} - httpConfig.Address = config.Address - httpConfig.TLS = config.TLS - httpConfig.CertFile = config.CertFile - httpConfig.KeyFile = config.KeyFile - - httpConfig.Websocket = k.Websocket - ctrlChan, statusChan, _ = httpserver.Start(web.HTTPHandler(), httpConfig) - - ctrlChans = append(ctrlChans, ctrlChan) - statusChans = append(statusChans, statusChan) - } - default: - { - err := iodine.New(errors.New("Invalid API type"), nil) - log.Fatal(err) - } - } - } - return -} - -func getDriverChannels(driverType DriverType) (ctrlChans []chan<- string, statusChans []<-chan error, driver drivers.Driver) { - // a pair of control channels, we use these primarily to add to the lists above - var ctrlChan chan<- string - var statusChan <-chan error - - // instantiate driver - // preconditions: - // - driver type specified - // - any configuration for driver is populated - // postconditions: - // - driver driver is initialized - // - ctrlChans has channel to communicate to driver - // - statusChans has channel for messages coming from driver - switch { - case driverType == Memory: - { - ctrlChan, statusChan, driver = memory.Start(1024 * 1024 * 1024) - ctrlChans = append(ctrlChans, ctrlChan) - statusChans = append(statusChans, statusChan) - } - case driverType == Donut: - { - u, err := user.Current() - if err != nil { - log.Error.Println(iodine.New(err, nil)) - return nil, nil, nil - } - root := path.Join(u.HomeDir, "minio-storage", "donut") - ctrlChan, statusChan, driver = donut.Start(root) - ctrlChans = append(ctrlChans, ctrlChan) - statusChans = append(statusChans, statusChan) - } - default: // should never happen - { - log.Fatal(iodine.New(errors.New("No driver found"), nil)) - } - } - return -} - -// Start - create channels -func Start(configs []Config) { - // reflected looping is necessary to remove dead channels from loop and not flood switch - ctrlChans, statusChans := getHTTPChannels(configs) - cases := createSelectCases(statusChans) - for len(cases) > 0 { - chosen, value, recvOk := reflect.Select(cases) - switch recvOk { - case true: - // Status Message Received - switch true { - case value.Interface() != nil: - // For any error received cleanup all existing channels and fail - for _, ch := range ctrlChans { - close(ch) - } - msg := fmt.Sprintf("%q", value.Interface()) - log.Fatal(iodine.New(errors.New(msg), nil)) - } - case false: - // Channel closed, remove from list - var aliveStatusChans []<-chan error - for i, ch := range statusChans { - if i != chosen { - aliveStatusChans = append(aliveStatusChans, ch) - } - } - // create new select cases without defunct channel - statusChans = aliveStatusChans - cases = createSelectCases(statusChans) - } - } -} - -// creates select cases for reflect to switch over dynamically -// this is necessary in order to remove dead channels and not flood -// the loop with closed channel errors -func createSelectCases(channels []<-chan error) []reflect.SelectCase { - cases := make([]reflect.SelectCase, len(channels)) - for i, ch := range channels { - cases[i] = reflect.SelectCase{ - Dir: reflect.SelectRecv, - Chan: reflect.ValueOf(ch), - } - } - return cases -}