Skip to content

Commit

Permalink
Get config yaml route
Browse files Browse the repository at this point in the history
  • Loading branch information
henrod committed Jun 16, 2017
1 parent 7551627 commit 5d97f1b
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 24 deletions.
39 changes: 20 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,41 +68,42 @@ The config file must have the following information:
Example yaml config:

```yaml
name: pong-free-for-all # this will be the name of the kubernetes namespace (it must be unique)
game: pong # several configs can refer to the same game
name: pong-free-for-all # this will be the name of the kubernetes namespace (it must be unique)
game: pong # several configs can refer to the same game
image: pong/pong:v123
affinity: node-affinity # optional field: if set, rooms will be allocated preferentially to nodes with label "node-affinity": "true"
affinity: node-affinity # optional field: if set, rooms will be allocated preferentially to nodes with label "node-affinity": "true"
toleration: node-toleration # optional field: if set, rooms will also be allocated in nodes with this taint
ports:
- containerPort: 5050 # port exposed in the container
protocol: UDP # supported protocols are TCP and UDP
name: gamebinary # name identifying the port (must be unique for a config)
- containerPort: 5050 # port exposed in the container
protocol: UDP # supported protocols are TCP and UDP
name: gamebinary # name identifying the port (must be unique for a config)
- containerPort: 8888
protocol: TCP
name: websocket
limits: # these will be the resources limits applied to the pods created in kubernetes
memory: "128Mi" # they are used to decide how many rooms can run in each node
cpu: "1" # more info: https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-ram-container/
shutdownTimeout: 180 # duration in seconds the pod needs to terminate gracefully
limits: # these will be the resources limits applied to the pods created in kubernetes
memory: "128Mi" # they are used to decide how many rooms can run in each node
cpu: "1" # more info: https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-ram-container/
shutdownTimeout: 180 # duration in seconds the pod needs to terminate gracefully
autoscaling:
min: 100 # minimum amount of GRUs
min: 100 # minimum amount of GRUs
up:
delta: 10 # how many GRUs will be created every time the scaling policy is triggered
delta: 10 # how many GRUs will be created every time the scaling policy is triggered
trigger:
usage: 70 # minimum usage (percentage) that can trigger the scaling policy
time: 600 # duration in seconds to wait before scaling policy takes place
cooldown: 300 # duration in seconds to wait before consecutive scaling
usage: 70 # minimum usage (percentage) that can trigger the scaling policy
time: 600 # duration in seconds to wait before scaling policy takes place
cooldown: 300 # duration in seconds to wait before consecutive scaling
down:
delta: 2 # how many GRUs will be terminated every time the scaling policy is triggered
delta: 2 # how many GRUs will be terminated every time the scaling policy is triggered
trigger:
usage: 50 # maximum usage (percentage) the can trigger the scaling policy
usage: 50 # maximum usage (percentage) the can trigger the scaling policy
time: 900
cooldown: 300
env: # environment variable to be passed on to the container
env: # environment variable to be passed on to the container
- name: EXAMPLE_ENV_VAR
value: examplevalue
- name: ANOTHER_ENV_VAR
value: anothervalue
cmd: # if the image can run with different arguments you can specify a cmd
cmd: # if the image can run with different arguments you can specify a cmd
- "./room-binary"
- "-serverType"
- "6a8e136b-2dc1-417e-bbe8-0f0a2d2df431"
Expand Down
55 changes: 55 additions & 0 deletions api/scheduler_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,13 @@ func (g *SchedulerStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
l := loggerFromContext(r.Context())
mr := metricsReporterFromCtx(r.Context())
params := schedulerParamsFromContext(r.Context())

_, getConfig := r.URL.Query()["config"]
if getConfig {
g.returnConfig(w, r, l, mr, params.SchedulerName)
return
}

logger := l.WithFields(logrus.Fields{
"source": "schedulerHandler",
"operation": "status",
Expand Down Expand Up @@ -292,3 +299,51 @@ func (g *SchedulerStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
})
logger.Debug("Status scheduler succeeded.")
}

func (g *SchedulerStatusHandler) returnConfig(
w http.ResponseWriter,
r *http.Request,
l logrus.FieldLogger,
mr *models.MixedMetricsReporter,
schedulerName string,
) {
logger := l.WithFields(logrus.Fields{
"source": "schedulerHandler",
"operation": "get config",
})

logger.Debugf("Getting scheduler %s config", schedulerName)

var yamlStr string
err := mr.WithSegment(models.SegmentSelect, func() error {
var err error
yamlStr, err = models.LoadConfig(g.App.DB, schedulerName)
return err
})
if err != nil {
logger.WithError(err).Error("config scheduler failed.")
g.App.HandleError(w, http.StatusInternalServerError, "config scheduler failed", err)
return
}
if len(yamlStr) == 0 {
logger.Error("config scheduler not found.")
g.App.HandleError(w, http.StatusNotFound, "get config error", errors.New("config scheduler not found"))
return
}
resp := map[string]interface{}{
"yaml": yamlStr,
}

bts, err := json.Marshal(resp)
if err != nil {
logger.WithError(err).Error("config scheduler failed.")
g.App.HandleError(w, http.StatusInternalServerError, "config scheduler failed", err)
return
}

mr.WithSegment(models.SegmentSerialization, func() error {
WriteBytes(w, http.StatusOK, bts)
return nil
})
logger.Debug("config scheduler succeeded.")
}
72 changes: 72 additions & 0 deletions api/scheduler_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,78 @@ var _ = Describe("Scheduler Handler", func() {
Expect(obj["success"]).To(Equal(false))
})
})

Describe("GET /scheduler/{schedulerName}?config", func() {
yamlStr := `
name: scheduler-name
game: game-name
`
schedulerName := "scheduler-name"

It("should return yaml config", func() {
url := fmt.Sprintf("http://%s/scheduler/%s?config", app.Address, schedulerName)
request, err := http.NewRequest("GET", url, nil)
Expect(err).NotTo(HaveOccurred())

mockDb.EXPECT().
Query(gomock.Any(), "SELECT yaml FROM schedulers WHERE name = ?", schedulerName).
Do(func(scheduler *models.Scheduler, query string, modifier string) {
scheduler.YAML = yamlStr
})

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Code).To(Equal(http.StatusOK))

resp := make(map[string]interface{})
err = json.Unmarshal(recorder.Body.Bytes(), &resp)
Expect(err).NotTo(HaveOccurred())
Expect(resp).To(HaveKeyWithValue("yaml", yamlStr))
})

It("should return 500 if db fails", func() {
url := fmt.Sprintf("http://%s/scheduler/%s?config", app.Address, schedulerName)
request, err := http.NewRequest("GET", url, nil)
Expect(err).NotTo(HaveOccurred())

mockDb.EXPECT().
Query(gomock.Any(), "SELECT yaml FROM schedulers WHERE name = ?", schedulerName).
Return(nil, errors.New("db error"))

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Code).To(Equal(http.StatusInternalServerError))

resp := make(map[string]interface{})
err = json.Unmarshal(recorder.Body.Bytes(), &resp)
Expect(err).NotTo(HaveOccurred())
Expect(resp).To(HaveKeyWithValue("code", "MAE-000"))
Expect(resp).To(HaveKeyWithValue("description", "db error"))
Expect(resp).To(HaveKeyWithValue("error", "config scheduler failed"))
Expect(resp).To(HaveKeyWithValue("success", false))
})

It("should return 404 if scheduler doesn't exists", func() {
url := fmt.Sprintf("http://%s/scheduler/%s?config", app.Address, schedulerName)
request, err := http.NewRequest("GET", url, nil)
Expect(err).NotTo(HaveOccurred())

mockDb.EXPECT().
Query(gomock.Any(), "SELECT yaml FROM schedulers WHERE name = ?", schedulerName).
Do(func(scheduler *models.Scheduler, query string, modifier string) {
scheduler.YAML = ""
})

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Code).To(Equal(http.StatusNotFound))

resp := make(map[string]interface{})
err = json.Unmarshal(recorder.Body.Bytes(), &resp)
Expect(err).NotTo(HaveOccurred())
Expect(resp).To(HaveKeyWithValue("code", "MAE-000"))
Expect(resp).To(HaveKeyWithValue("description", "config scheduler not found"))
Expect(resp).To(HaveKeyWithValue("error", "get config error"))
Expect(resp).To(HaveKeyWithValue("success", false))
})
})
})

Context("When authentication fails", func() {
Expand Down
48 changes: 47 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ All API responses include a `X-Maestro-Version` header with the current Maestro

* Error Response

It will return an error if scheduler to update is not found on DB
It will return an error if scheduler is not found on DB

* Code: `404`
* Content:
Expand Down Expand Up @@ -624,3 +624,49 @@ All API responses include a `X-Maestro-Version` header with the current Maestro
"success": [bool]false
}
```

### Scheduler Config

`GET /scheduler/:schedulerName?config`

Returns scheduler config.

* Success Response
* Code: `200`
* Content:

```
{
"yaml": [string]<yaml-config>,
}
```

* Error Response

It will return an error if scheduler is not found on DB

* Code: `404`
* Content:

```
{
"code": [string]<error-code>,
"error": [string]<error-message>,
"description": [string]<error-description>,
"success": [bool]false
}
```

It will return an error if some other error occurred.

* Code: `500`
* Content:

```
{
"code": [string]<error-code>,
"error": [string]<error-message>,
"description": [string]<error-description>,
"success": [bool]false
}
```
2 changes: 1 addition & 1 deletion helm/charts/maestro/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: maestro
home: https://github.com/topfreegames/maestro
description: Maestro api and worker
version: 2.9.1
version: 2.10.0
maintainers:
- name: TFGCo
email: backend@tfgco.com
4 changes: 2 additions & 2 deletions migrations/migrations.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion models/pod_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ var _ = Describe("Pod", func() {
command,
env,
)
pod.SetToleration(game)
podv1, err := pod.Create(clientset)
Expect(err).NotTo(HaveOccurred())

Expand All @@ -111,7 +112,7 @@ var _ = Describe("Pod", func() {
Expect(podv1.ObjectMeta.Labels["app"]).To(Equal(name))
Expect(*podv1.Spec.TerminationGracePeriodSeconds).To(BeEquivalentTo(shutdownTimeout))
Expect(podv1.Spec.Tolerations).To(HaveLen(1))
Expect(podv1.Spec.Tolerations[0].Key).To(Equal("game"))
Expect(podv1.Spec.Tolerations[0].Key).To(Equal("dedicated"))
Expect(podv1.Spec.Tolerations[0].Operator).To(Equal(v1.TolerationOpEqual))
Expect(podv1.Spec.Tolerations[0].Value).To(Equal(game))
Expect(podv1.Spec.Tolerations[0].Effect).To(Equal(v1.TaintEffectNoSchedule))
Expand Down
6 changes: 6 additions & 0 deletions models/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,9 @@ func ListSchedulersNames(db interfaces.DB) ([]string, error) {
}
return names, nil
}

func LoadConfig(db interfaces.DB, schedulerName string) (string, error) {
c := new(Scheduler)
_, err := db.Query(c, "SELECT yaml FROM schedulers WHERE name = ?", schedulerName)
return c.YAML, err
}
15 changes: 15 additions & 0 deletions models/scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,19 @@ var _ = Describe("Scheduler", func() {
Expect(err.Error()).To(Equal("some error in pg"))
})
})

Describe("LoadConfig", func() {
It("should get schedulers yaml from the database", func() {
yamlStr := "scheduler: example"
name := "scheduler-name"
mockDb.EXPECT().Query(gomock.Any(), "SELECT yaml FROM schedulers WHERE name = ?", name).Do(
func(scheduler *models.Scheduler, query string, name string) {
scheduler.YAML = yamlStr
},
)
yamlRes, err := models.LoadConfig(mockDb, name)
Expect(err).NotTo(HaveOccurred())
Expect(yamlRes).To(Equal(yamlStr))
})
})
})
1 change: 1 addition & 0 deletions testing/minikube.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func NextJsonStr() (string, error) {
"name": "{{.Name}}",
"game": "game-name",
"image": "nginx:alpine",
"toleration": "game-name",
"ports": [
{
"containerPort": 8080,
Expand Down

0 comments on commit 5d97f1b

Please sign in to comment.