github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/plugin/manager/cmd/plugin.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net/http"
     7  
     8  	"github.com/emicklei/go-restful"
     9  	"github.com/jinzhu/gorm"
    10  	"github.com/nats-io/nats"
    11  )
    12  
    13  type PluginError struct {
    14  	Code string
    15  	Msg  string
    16  }
    17  
    18  type Plugin struct {
    19  	Name        string
    20  	Kind        string
    21  	Status      string
    22  	Description string
    23  	Spec        string
    24  	Manual      string
    25  }
    26  
    27  type Strategy struct {
    28  	//witch plugin strategy belongs to
    29  	PluginName string
    30  	Name       string
    31  	Status     string
    32  	Document   string
    33  }
    34  
    35  type PluginResource struct {
    36  	db *gorm.DB
    37  	mq *nats.EncodedConn
    38  }
    39  
    40  type ScaleApp struct {
    41  	App    string
    42  	Number int
    43  }
    44  
    45  const (
    46  	COMMAND_START_PLUGIN     = "start-plugin"
    47  	COMMAND_STOP_PLUGIN      = "stop-plugin"
    48  	COMMAND_ENABLE_STRATEGY  = "enable-strategy"
    49  	COMMAND_DISABLE_STRATEGY = "disable-strategy"
    50  	COMMAND_UPDATE_DOCUMENT  = "update-document"
    51  )
    52  
    53  const (
    54  	PLUGIN_ENABLE  = "enable"
    55  	PLUGIN_DISABLE = "disable"
    56  
    57  	STRATEGY_ENABLE  = "enable"
    58  	STRATEGY_DISABLE = "disable"
    59  )
    60  
    61  type Command struct {
    62  	Command string
    63  	Channel string // each plugin subscribe it plugin name, plugin name is channel
    64  	Body    string
    65  }
    66  
    67  func (p PluginResource) Register(container *restful.Container) {
    68  	ws := new(restful.WebService)
    69  
    70  	ws.
    71  		Path("/plugins").
    72  		Doc("Manage plugins").
    73  		Consumes(restful.MIME_JSON).
    74  		Produces(restful.MIME_JSON)
    75  
    76  	ws.Route(ws.POST("").To(p.createPlugin).
    77  		Doc("create a plugin").
    78  		Operation("createPlugin").
    79  		Reads(Plugin{}))
    80  
    81  	ws.Route(ws.GET("").To(p.listPlugins).
    82  		Doc("list plugins").
    83  		Operation("listPlugins").
    84  		Returns(200, "OK", []Plugin{}))
    85  
    86  	ws.Route(ws.GET("/{pluginName}").To(p.getPluginDetail).
    87  		Doc("get plugin detail").
    88  		Operation("getPluginDetail").
    89  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
    90  		Writes(Plugin{}))
    91  
    92  	ws.Route(ws.PUT("/{pluginName}").To(p.updatePlugin).
    93  		Doc("update a plugin").
    94  		Operation("updatePlugin").
    95  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
    96  		ReturnsError(409, "duplicate user-id", nil).
    97  		Reads(Plugin{}))
    98  
    99  	ws.Route(ws.DELETE("/{pluginName}").To(p.deletePlugin).
   100  		Doc("delete plugin").
   101  		Operation("deletePlugin").
   102  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")))
   103  
   104  	//Plugin strateg
   105  	ws.Route(ws.POST("/{pluginName}/strategies").To(p.createPluginStrategies).
   106  		Doc("create plugin strategies").
   107  		Operation("createPluginStrategies").
   108  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
   109  		Reads(Strategy{}))
   110  
   111  	ws.Route(ws.GET("/{pluginName}/strategies").To(p.listPluginStragies).
   112  		Doc("list plugin strategies").
   113  		Operation("listPluginStragies").
   114  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
   115  		Returns(200, "OK", []Strategy{}))
   116  
   117  	ws.Route(ws.GET("/{pluginName}/strategies/{strategyName}").
   118  		To(p.getPluginStrategyDetail).
   119  		Doc("get strategy detail").
   120  		Operation("getStrategy").
   121  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
   122  		Param(ws.PathParameter("strategyName", "name of strategy").DataType("string")).
   123  		Writes(Strategy{}))
   124  
   125  	ws.Route(ws.PUT("/{pluginName}/strategies/{strategyName}").
   126  		To(p.updatePluginStrategy).
   127  		Doc("update a plugin strategy").
   128  		Operation("updatePluginStrategy").
   129  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
   130  		Param(ws.PathParameter("StrategyName", "name of plugin strategy").DataType("string")).
   131  		ReturnsError(409, "duplicate strategy id", nil).
   132  		Reads(Strategy{}))
   133  
   134  	ws.Route(ws.DELETE("/{pluginName}/strategies/{strategyName}").
   135  		To(p.deletePluginStrategy).
   136  		Doc("delete plugin strategy").
   137  		Operation("deletePluginStrategy").
   138  		Param(ws.PathParameter("pluginName", "name of plugin").DataType("string")).
   139  		Param(ws.PathParameter("strategyName", "name of plugin strategy").DataType("string")))
   140  
   141  	ws.Route(ws.POST("/scale").To(p.scaleApp).
   142  		Doc("scale app").
   143  		Operation("scaleApp").
   144  		Reads([]ScaleApp{}))
   145  
   146  	container.Add(ws)
   147  }
   148  
   149  func (this PluginResource) createPlugin(request *restful.Request, response *restful.Response) {
   150  	p := new(Plugin)
   151  	res := Plugin{Name: ""}
   152  
   153  	err := request.ReadEntity(p)
   154  
   155  	if err != nil {
   156  		response.AddHeader("Content-Type", "text/plain")
   157  		response.WriteErrorString(http.StatusInternalServerError, err.Error())
   158  		return
   159  	}
   160  
   161  	//query result will write to res, if already exist res.Name will change
   162  	//then quit the insert action
   163  	if this.db.Where("name = ?", p.Name).First(&res); res.Name == "" {
   164  		this.db.NewRecord(p)
   165  		this.db.Create(&p)
   166  	} else {
   167  		log.Printf("plugin %s already exist", p.Name)
   168  		response.WriteHeaderAndEntity(http.StatusBadRequest, PluginError{"400", "already exit"})
   169  		return
   170  	}
   171  
   172  	response.WriteHeaderAndEntity(http.StatusCreated, p)
   173  }
   174  
   175  func (this PluginResource) listPlugins(request *restful.Request, response *restful.Response) {
   176  	plugins := []Plugin{}
   177  
   178  	this.db.Find(&plugins)
   179  
   180  	response.WriteEntity(plugins)
   181  }
   182  
   183  func (this PluginResource) getPluginDetail(request *restful.Request,
   184  	response *restful.Response) {
   185  
   186  	plugin := Plugin{Name: ""}
   187  	pluginName := request.PathParameter("pluginName")
   188  
   189  	this.db.Where("name = ?", pluginName).First(&plugin)
   190  	if plugin.Name != "" {
   191  		response.WriteEntity(plugin)
   192  		return
   193  	}
   194  	response.WriteHeaderAndEntity(http.StatusBadRequest,
   195  		PluginError{"400", "plugin not found"})
   196  }
   197  
   198  func (this PluginResource) updatePlugin(request *restful.Request, response *restful.Response) {
   199  	pluginName := request.PathParameter("pluginName")
   200  	plugin := Plugin{}
   201  	tmp := Plugin{}
   202  	err := request.ReadEntity(&plugin)
   203  
   204  	//this.db.Where("name = ?", pluginName).Save(&plugin)
   205  	this.db.Where("name = ?", pluginName).First(&tmp)
   206  
   207  	if plugin.Status != tmp.Status {
   208  		this.db.Model(&tmp).Where("name = ?", pluginName).Update("status", plugin.Status)
   209  
   210  		// send command to plugin
   211  		if plugin.Status == PLUGIN_ENABLE {
   212  			this.mq.Publish(pluginName, Command{COMMAND_START_PLUGIN, pluginName, ""})
   213  		} else if plugin.Status == PLUGIN_DISABLE {
   214  			this.mq.Publish(pluginName, Command{COMMAND_STOP_PLUGIN, pluginName, ""})
   215  		}
   216  	}
   217  	if plugin.Kind != tmp.Kind {
   218  		this.db.Model(&tmp).Where("name = ?", pluginName).Update("kind", plugin.Kind)
   219  	}
   220  	if plugin.Description != tmp.Description {
   221  		this.db.Model(&tmp).Where("name = ?", pluginName).Update("description", plugin.Description)
   222  	}
   223  	if plugin.Spec != tmp.Spec {
   224  		this.db.Model(&tmp).Where("name = ?", pluginName).Update("spec", plugin.Spec)
   225  	}
   226  	if plugin.Manual != tmp.Manual {
   227  		this.db.Model(&tmp).Where("name = ?", pluginName).Update("manual", plugin.Manual)
   228  	}
   229  
   230  	if err != nil {
   231  		response.AddHeader("Content-type", "text/plain")
   232  		response.WriteErrorString(http.StatusInternalServerError, err.Error())
   233  		return
   234  	}
   235  	response.WriteEntity(tmp)
   236  }
   237  
   238  func (this PluginResource) deletePlugin(request *restful.Request, response *restful.Response) {
   239  	pluginName := request.PathParameter("pluginName")
   240  
   241  	this.db.Where("name = ?", pluginName).Delete(Plugin{})
   242  
   243  	// stop plugin
   244  	this.mq.Publish(pluginName, Command{COMMAND_STOP_PLUGIN, pluginName, ""})
   245  
   246  	response.WriteHeaderAndEntity(http.StatusOK, PluginError{"0", "delete ok"})
   247  }
   248  
   249  func (this PluginResource) createPluginStrategies(request *restful.Request,
   250  	response *restful.Response) {
   251  
   252  	//pluginName := request.PathParameter("pluginName")
   253  	strategy := new(Strategy)
   254  	res := Strategy{Name: ""}
   255  
   256  	err := request.ReadEntity(strategy)
   257  	if err != nil {
   258  		response.AddHeader("Content-Type", "text/plain")
   259  		response.WriteErrorString(http.StatusInternalServerError, err.Error())
   260  		return
   261  	}
   262  
   263  	if this.db.Where("name = ?", strategy.Name).First(&res); res.Name == "" {
   264  		this.db.NewRecord(strategy)
   265  		this.db.Create(&strategy)
   266  	} else {
   267  		log.Printf("plugin %s already exist", strategy.Name)
   268  		response.WriteHeaderAndEntity(http.StatusBadRequest, PluginError{"400", "already exit"})
   269  		return
   270  	}
   271  
   272  	response.WriteHeaderAndEntity(http.StatusCreated, strategy)
   273  
   274  }
   275  
   276  func (this PluginResource) listPluginStragies(request *restful.Request,
   277  	response *restful.Response) {
   278  
   279  	pluginName := request.PathParameter("pluginName")
   280  	strategies := []Strategy{}
   281  
   282  	this.db.Where("plugin_name = ?", pluginName).Find(&strategies)
   283  
   284  	response.WriteEntity(strategies)
   285  }
   286  
   287  func (this PluginResource) getPluginStrategyDetail(request *restful.Request,
   288  	response *restful.Response) {
   289  
   290  	strategy := Strategy{Name: ""}
   291  	strategyName := request.PathParameter("strategyName")
   292  
   293  	this.db.Where("name = ?", strategyName).First(&strategy)
   294  	if strategy.Name != "" {
   295  		response.WriteEntity(strategy)
   296  		return
   297  	}
   298  	response.WriteHeaderAndEntity(http.StatusBadRequest,
   299  		PluginError{"400", "plugin strategy not found"})
   300  }
   301  
   302  func (this PluginResource) updatePluginStrategy(request *restful.Request,
   303  	response *restful.Response) {
   304  
   305  	strategyName := request.PathParameter("strategyName")
   306  	pluginName := request.PathParameter("pluginName")
   307  
   308  	strategy := Strategy{}
   309  	tmp := Strategy{}
   310  	err := request.ReadEntity(&strategy)
   311  
   312  	//this.db.Where("name = ?", pluginName).Save(&plugin)
   313  	this.db.Where("name = ?", strategyName).First(&tmp)
   314  
   315  	if strategy.Status != tmp.Status {
   316  		this.db.Model(&tmp).Where("name = ?", strategyName).Update("status", strategy.Status)
   317  
   318  		fmt.Println("change strategy status: ", strategy.Status, " plugin Name: ", pluginName)
   319  		// send command to plugin
   320  		if strategy.Status == STRATEGY_ENABLE {
   321  			this.mq.Publish(pluginName, Command{COMMAND_ENABLE_STRATEGY, pluginName, strategyName})
   322  		} else if strategy.Status == STRATEGY_DISABLE {
   323  			this.mq.Publish(pluginName, Command{COMMAND_DISABLE_STRATEGY, pluginName, strategyName})
   324  		}
   325  	}
   326  	if strategy.Document != tmp.Document {
   327  		this.db.Model(&tmp).Where("name = ?", strategyName).Update("document", strategy.Document)
   328  		this.mq.Publish(pluginName, Command{COMMAND_UPDATE_DOCUMENT, pluginName, strategyName})
   329  	}
   330  
   331  	if err != nil {
   332  		response.AddHeader("Content-type", "text/plain")
   333  		response.WriteErrorString(http.StatusInternalServerError, err.Error())
   334  		return
   335  	}
   336  	response.WriteEntity(tmp)
   337  }
   338  
   339  func (this PluginResource) deletePluginStrategy(request *restful.Request,
   340  	response *restful.Response) {
   341  
   342  	strategyName := request.PathParameter("strategyName")
   343  	pluginName := request.PathParameter("pluginName")
   344  
   345  	this.db.Where("name = ?", strategyName).Delete(Strategy{})
   346  
   347  	this.mq.Publish(pluginName, Command{COMMAND_DISABLE_STRATEGY, pluginName, strategyName})
   348  
   349  	response.WriteHeaderAndEntity(http.StatusOK, PluginError{"0", "delete ok"})
   350  }
   351  
   352  /*
   353  func (this PluginResource) scaleApp(request *restful.Request,
   354  	response *restful.Response) {
   355  
   356  	scaleApp := []ScaleApp{}
   357  
   358  	err := request.ReadEntity(&scaleApp)
   359  	if err != nil {
   360  	}
   361  
   362  	//	{
   363  //			"ats:latest":{2, 20}
   364  //			"hadoop:v1.0":{20, 2}
   365  //		}
   366  
   367  	scaleInfo := make(map[string]struct{Current int, Need int})
   368  
   369  	fmt.Println("scale: ", scaleApp)
   370  }
   371  */
   372  
   373  func runServer(host string, port string) {
   374  	wsContainer := restful.NewContainer()
   375  
   376  	db := initDB(DBHost, DBPort, DBUser, DBName, DBPasswd)
   377  	defer db.Close()
   378  
   379  	cors := restful.CrossOriginResourceSharing{
   380  		AllowedHeaders: []string{"Origin", "Content-Type", "Accept"},
   381  		AllowedDomains: []string{AllowedDomain},
   382  		AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
   383  		Container:      wsContainer,
   384  	}
   385  
   386  	wsContainer.Filter(cors.Filter)
   387  	wsContainer.Filter(wsContainer.OPTIONSFilter)
   388  
   389  	nc, _ := nats.Connect(nats.DefaultURL)
   390  	c, _ := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
   391  
   392  	p := PluginResource{db, c}
   393  	p.Register(wsContainer)
   394  
   395  	log.Printf("start listening on %s%s", host, port)
   396  	server := &http.Server{Addr: port, Handler: wsContainer}
   397  	log.Fatal(server.ListenAndServe())
   398  }