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 }