Skip to content

List active Cortex deployments #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions cli/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func init() {
addWatchFlag(getCmd)
addSummaryFlag(getCmd)
addVerboseFlag(getCmd)
addAllDeploymentsFlag(getCmd)
// addResourceTypesToHelp(getCmd)
}

Expand All @@ -63,6 +64,10 @@ var getCmd = &cobra.Command{
}

func runGet(cmd *cobra.Command, args []string) (string, error) {
if flagAllDeployments || !IsAppNameSpecified() {
return getDeploymentsResponse()
}

resourcesRes, err := getResourcesResponse()
if err != nil {
return "", err
Expand Down Expand Up @@ -109,6 +114,42 @@ func runGet(cmd *cobra.Command, args []string) (string, error) {
return "", errors.New("too many args") // unexpected
}

func getDeploymentsResponse() (string, error) {
httpResponse, err := HTTPGet("/deployments", map[string]string{})
if err != nil {
return "", err
}

var resourcesRes schema.GetDeploymentsResponse
if err = json.Unmarshal(httpResponse, &resourcesRes); err != nil {
return "", err
}

if len(resourcesRes.Deployments) == 0 {
return "No deployments found", nil
}

rows := make([][]interface{}, len(resourcesRes.Deployments))
for idx, deployment := range resourcesRes.Deployments {
rows[idx] = []interface{}{
deployment.Name,
deployment.Status.String(),
libtime.Since(&deployment.LastUpdated),
}
}

t := table.Table{
Headers: []table.Header{
{Title: "NAME", MaxWidth: 32},
{Title: "STATUS", MaxWidth: 21},
{Title: "LAST UPDATED"},
},
Rows: rows,
}

return table.MustFormat(t), nil
}

func getResourcesResponse() (*schema.GetResourcesResponse, error) {
appName, err := AppNameFromFlagOrConfig()
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions cli/cmd/lib_config_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@ func AppNameFromFlagOrConfig() (string, error) {

return appName, nil
}

func IsAppNameSpecified() bool {
return flagAppName != "" || appRootOrBlank() != ""
}
5 changes: 5 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var flagWatch bool
var flagAppName string
var flagVerbose bool
var flagSummary bool
var flagAllDeployments bool

var configFileExts = []string{"yaml", "yml"}

Expand Down Expand Up @@ -106,6 +107,10 @@ func addSummaryFlag(cmd *cobra.Command) {
cmd.PersistentFlags().BoolVarP(&flagSummary, "summary", "s", false, "show summarized output")
}

func addAllDeploymentsFlag(cmd *cobra.Command) {
getCmd.PersistentFlags().BoolVarP(&flagAllDeployments, "all-deployments", "a", false, "list all deployments")
}

var resourceTypesHelp = fmt.Sprintf("\nResource Types:\n %s\n", strings.Join(resource.VisibleTypes.StringList(), "\n "))

func addResourceTypesToHelp(cmd *cobra.Command) {
Expand Down
3 changes: 2 additions & 1 deletion docs/cluster/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Usage:
cortex get [RESOURCE_TYPE] [RESOURCE_NAME] [flags]

Flags:
-a, --all-deployments list all deployments
-d, --deployment string deployment name
-e, --env string environment (default "dev")
-h, --help help for get
Expand All @@ -33,7 +34,7 @@ Flags:
-w, --watch re-run the command every second
```

The `get` command displays the current state of all resources on the cluster. Specifying a resource name provides the state of the particular resource. A detailed view of the configuration and additional metdata of a specific resource can be retrieved by adding the `-v` or `--verbose` flag. Using the `-s` or `--summary` flag will show a summarized view of all resource statuses.
The `get` command displays the current state of all resources on the cluster. Specifying a resource name provides the state of the particular resource. A detailed view of the configuration and additional metdata of a specific resource can be retrieved by adding the `-v` or `--verbose` flag. Using the `-s` or `--summary` flag will show a summarized view of all resource statuses. A list of deployments can be displayed by specifying the `-a` or `--all-deployments` flag.

## logs

Expand Down
1 change: 1 addition & 0 deletions pkg/operator/api/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
type Context struct {
ID string `json:"id"`
Key string `json:"key"`
CreatedEpoch int64 `json:"created_epoch"`
CortexConfig *config.CortexConfig `json:"cortex_config"`
DatasetVersion string `json:"dataset_version"`
Root string `json:"root"`
Expand Down
80 changes: 80 additions & 0 deletions pkg/operator/api/resource/deployment_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright 2019 Cortex Labs, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resource

type DeploymentStatus int

const (
UnknownDeploymentStatus DeploymentStatus = iota
UpdatedDeploymentStatus
UpdatingDeploymentStatus
ErrorDeploymentStatus
)

var deploymentStatuses = []string{
"unknown",
"updated",
"updating",
"error",
}

func DeploymentStatusFromString(s string) DeploymentStatus {
for i := 0; i < len(deploymentStatuses); i++ {
if s == deploymentStatuses[i] {
return DeploymentStatus(i)
}
}
return UnknownDeploymentStatus
}

func DeploymentStatusStrings() []string {
return deploymentStatuses[1:]
}

func (t DeploymentStatus) String() string {
return deploymentStatuses[t]
}

// MarshalText satisfies TextMarshaler
func (t DeploymentStatus) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (t *DeploymentStatus) UnmarshalText(text []byte) error {
enum := string(text)
for i := 0; i < len(deploymentStatuses); i++ {
if enum == deploymentStatuses[i] {
*t = DeploymentStatus(i)
return nil
}
}

*t = UnknownDeploymentStatus
return nil
}

// UnmarshalBinary satisfies BinaryUnmarshaler
// Needed for msgpack
func (t *DeploymentStatus) UnmarshalBinary(data []byte) error {
return t.UnmarshalText(data)
}

// MarshalBinary satisfies BinaryMarshaler
func (t DeploymentStatus) MarshalBinary() ([]byte, error) {
return []byte(t.String()), nil
}
12 changes: 12 additions & 0 deletions pkg/operator/api/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package schema

import (
"time"

"github.com/cortexlabs/cortex/pkg/operator/api/context"
"github.com/cortexlabs/cortex/pkg/operator/api/resource"
)
Expand All @@ -41,6 +43,16 @@ type GetResourcesResponse struct {
APIsBaseURL string `json:"apis_base_url"`
}

type Deployment struct {
Name string `json:"name"`
Status resource.DeploymentStatus `json:"status"`
LastUpdated time.Time `json:"last_updated"`
}

type GetDeploymentsResponse struct {
Deployments []Deployment `json:"deployments"`
}

type GetAggregateResponse struct {
Value []byte `json:"value"`
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/operator/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"path/filepath"
"sort"
"strings"
"time"

"github.com/cortexlabs/cortex/pkg/consts"
"github.com/cortexlabs/cortex/pkg/lib/configreader"
Expand Down Expand Up @@ -122,6 +123,7 @@ func New(
ignoreCache bool,
) (*context.Context, error) {
ctx := &context.Context{}
ctx.CreatedEpoch = time.Now().Unix()

ctx.CortexConfig = config.Cortex

Expand Down
5 changes: 4 additions & 1 deletion pkg/operator/endpoints/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/cortexlabs/cortex/pkg/lib/files"
"github.com/cortexlabs/cortex/pkg/lib/zip"
"github.com/cortexlabs/cortex/pkg/operator/api/context"
"github.com/cortexlabs/cortex/pkg/operator/api/resource"
"github.com/cortexlabs/cortex/pkg/operator/api/schema"
"github.com/cortexlabs/cortex/pkg/operator/api/userconfig"
"github.com/cortexlabs/cortex/pkg/operator/config"
Expand Down Expand Up @@ -61,12 +62,14 @@ func Deploy(w http.ResponseWriter, r *http.Request) {
fullCtxMatch = true
}

isUpdating, err := workloads.IsDeploymentUpdating(ctx.App.Name)
deploymentStatus, err := workloads.GetDeploymentStatus(ctx.App.Name)
if err != nil {
RespondError(w, err)
return
}

isUpdating := deploymentStatus == resource.UpdatingDeploymentStatus

if isUpdating {
if fullCtxMatch {
respondDeploy(w, ResDeploymentUpToDateUpdating)
Expand Down
42 changes: 42 additions & 0 deletions pkg/operator/endpoints/deployments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright 2019 Cortex Labs, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package endpoints

import (
"net/http"
"time"

"github.com/cortexlabs/cortex/pkg/operator/api/schema"
"github.com/cortexlabs/cortex/pkg/operator/workloads"
)

func GetDeployments(w http.ResponseWriter, r *http.Request) {
currentContexts := workloads.CurrentContexts()
deployments := make([]schema.Deployment, len(currentContexts))
for i, ctx := range currentContexts {
deployments[i].Name = ctx.App.Name
status, _ := workloads.GetDeploymentStatus(ctx.App.Name)
deployments[i].Status = status
deployments[i].LastUpdated = time.Unix(ctx.CreatedEpoch, 0)
}

response := schema.GetDeploymentsResponse{
Deployments: deployments,
}

Respond(w, response)
}
1 change: 1 addition & 0 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func main() {

router.HandleFunc("/deploy", endpoints.Deploy).Methods("POST")
router.HandleFunc("/delete", endpoints.Delete).Methods("POST")
router.HandleFunc("/deployments", endpoints.GetDeployments).Methods("GET")
router.HandleFunc("/resources", endpoints.GetResources).Methods("GET")
router.HandleFunc("/aggregate/{id}", endpoints.GetAggregate).Methods("GET")
router.HandleFunc("/logs/read", endpoints.ReadLogs)
Expand Down
25 changes: 14 additions & 11 deletions pkg/operator/workloads/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/cortexlabs/cortex/pkg/lib/errors"
"github.com/cortexlabs/cortex/pkg/lib/sets/strset"
"github.com/cortexlabs/cortex/pkg/operator/api/context"
"github.com/cortexlabs/cortex/pkg/operator/api/resource"
"github.com/cortexlabs/cortex/pkg/operator/api/userconfig"
"github.com/cortexlabs/cortex/pkg/operator/config"
)
Expand Down Expand Up @@ -280,46 +281,48 @@ func IsWorkloadEnded(appName string, workloadID string) (bool, error) {
return false, errors.New("workload not found in the current context")
}

func IsDeploymentUpdating(appName string) (bool, error) {
func GetDeploymentStatus(appName string) (resource.DeploymentStatus, error) {
ctx := CurrentContext(appName)
if ctx == nil {
return false, nil
return resource.UnknownDeploymentStatus, nil
}

isUpdating := false
for _, workload := range extractWorkloads(ctx) {

// Pending HPA workloads shouldn't block new deployments
// HPA workloads don't really count
if workload.GetWorkloadType() == workloadTypeHPA {
continue
}

isSucceeded, err := workload.IsSucceeded(ctx)
if err != nil {
return false, err
return resource.UnknownDeploymentStatus, err
}
if isSucceeded {
continue
}

isFailed, err := workload.IsFailed(ctx)
if err != nil {
return false, err
return resource.UnknownDeploymentStatus, err
}
if isFailed {
continue
return resource.ErrorDeploymentStatus, nil
}

canRun, err := workload.CanRun(ctx)
if err != nil {
return false, err
return resource.UnknownDeploymentStatus, err
}
if !canRun {
continue
}

// It's either running or can run
return true, nil
isUpdating = true
}

return false, nil
if isUpdating {
return resource.UpdatingDeploymentStatus, nil
}
return resource.UpdatedDeploymentStatus, nil
}