Skip to content

Commit

Permalink
Add dependsOn feature to koyeb compose
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelbeza committed May 8, 2024
1 parent 3403fd9 commit db2f559
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 19 deletions.
23 changes: 20 additions & 3 deletions examples/koyeb-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: test-compose-service
name: compose-app
services:
compose-go-service:
go-service:
git:
repository: github.com/koyeb/example-golang
branch: main
Expand All @@ -18,4 +18,21 @@ services:
- scopes: ['region:par']
key: 'PORT'
value: '8000'

ping-service:
docker:
image: curlimages/curl
command: 'sh'
args: ['-c', 'while true; do curl go-service.compose-app.koyeb:8000; sleep 30; done']
type: 'WORKER'
regions: ['par']
scalings:
- scopes: ['region:par']
min: 1
max: 1
instance_types:
- scopes: ['region:par']
type: 'nano'
depends_on:
- go-service


7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module github.com/koyeb/koyeb-cli

go 1.18
go 1.21

require (
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/blang/semver v3.5.1+incompatible
github.com/briandowns/spinner v1.23.0
github.com/ghodss/yaml v1.0.0
github.com/gofrs/uuid v4.3.0+incompatible
github.com/gorilla/websocket v1.5.0
Expand All @@ -23,11 +24,11 @@ require (
github.com/spf13/viper v1.13.0
github.com/stretchr/testify v1.8.0
golang.org/x/term v0.8.0
gonum.org/v1/gonum v0.15.0
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/briandowns/spinner v1.23.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down Expand Up @@ -59,7 +60,7 @@ require (
golang.org/x/net v0.0.0-20220927171203-f486391704dc // indirect
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
Expand Down
15 changes: 12 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
Expand Down Expand Up @@ -126,7 +127,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo=
github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
Expand Down Expand Up @@ -175,9 +177,11 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
Expand Down Expand Up @@ -224,6 +228,7 @@ github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
Expand Down Expand Up @@ -288,6 +293,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -425,8 +432,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down Expand Up @@ -482,6 +489,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
Expand Down
16 changes: 16 additions & 0 deletions koyeb-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: test-compose-service
services:
myservicee:
docker:
image: nginx
ports:
- port: 80
regions: ['par']
scalings:
- scopes: ['region:par']
min: 1
max: 1
instance_types:
- scopes: ['region:par']
type: 'nano'

90 changes: 80 additions & 10 deletions pkg/koyeb/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package koyeb
import (
"fmt"
"os"
"slices"
"strings"
"time"

"github.com/briandowns/spinner"
Expand All @@ -12,6 +12,9 @@ import (
"github.com/koyeb/koyeb-cli/pkg/koyeb/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/simple"
"gonum.org/v1/gonum/graph/topo"
)

func NewComposeCmd() *cobra.Command {
Expand Down Expand Up @@ -64,6 +67,13 @@ func parseComposeFile(path string) (*KoyebCompose, error) {
return nil, err
}

for serviceName, serviceData := range config.Services {
// TODO (pawel) fix this hacky way of setting service name
serviceNameCopy := strings.Clone(serviceName)
serviceData.Name = &(serviceNameCopy)
config.Services[serviceName] = serviceData
}

return config, nil
}

Expand All @@ -90,34 +100,91 @@ func (h *KoyebComposeHandler) Compose(ctx *CLIContext, koyebCompose *KoyebCompos
return err
}

for serviceName, serviceDetails := range koyebCompose.Services {
serviceDefinition := &serviceDetails.DeploymentDefinition
serviceDefinition.Name = koyeb.PtrString(serviceName)
servicesOrdering, err := h.OrderServices(koyebCompose.Services)
if err != nil {
return err
}

deploymentId, err := h.UpdateService(ctx, appId, appName, serviceDefinition)
// TODO (pawel) parallelize deployments if possible (a matter of checking degree of service node in the graph)
for _, service := range servicesOrdering {
deploymentId, err := h.UpdateService(ctx, appId, appName, &service)
if err != nil {
return err
}

err = h.MonitorService(ctx, deploymentId, serviceName)
err = h.MonitorService(ctx, deploymentId, service.GetName())
if err != nil {
return err
}

}

log.Infof("\nYour app %v has been succesfully deployed 🚀", appName)

return nil
}

func (h *KoyebComposeHandler) isMonitoringEndState(status koyeb.DeploymentStatus) bool {
return slices.Contains([]koyeb.DeploymentStatus{
endStates := []koyeb.DeploymentStatus{
koyeb.DEPLOYMENTSTATUS_HEALTHY,
koyeb.DEPLOYMENTSTATUS_DEGRADED,
koyeb.DEPLOYMENTSTATUS_UNHEALTHY,
koyeb.DEPLOYMENTSTATUS_CANCELED,
koyeb.DEPLOYMENTSTATUS_STOPPED,
koyeb.DEPLOYMENTSTATUS_ERROR,
}, status)
}

for _, endState := range endStates {
if status == endState {
return true
}
}
return false
}

// orders services based on dependOn field (calculates topological order for services)
func (h *KoyebComposeHandler) OrderServices(services map[string]KoyebComposeService) ([]koyeb.DeploymentDefinition, error) {
dependencies := simple.NewDirectedGraph()
serviceNameToId := map[string]graph.Node{}
idToServiceName := map[graph.Node]string{}
for serviceName := range services {
serviceNode := dependencies.NewNode()

serviceNameToId[serviceName] = serviceNode
idToServiceName[serviceNode] = serviceName
dependencies.AddNode(serviceNode)
}

for serviceName, serviceDetails := range services {
for _, dependency := range serviceDetails.DependsOn {
fromNode := serviceNameToId[dependency]
toNode, ok := serviceNameToId[serviceName]
if !ok {
return nil, &errors.CLIError{
What: "failed to calculate deployment order of services",
Why: fmt.Sprintf("service %s depends on %s which is not defined", serviceName, dependency),
}
}

edge := dependencies.NewEdge(fromNode, toNode)
dependencies.SetEdge(edge)
}
}

servicesIdOrdering, err := topo.Sort(dependencies)
if err != nil {
return nil, &errors.CLIError{
What: "failed to calculate deployment order of services",
Why: "this probably indicates circular dependency",
}
}

servicesOrdering := make([]koyeb.DeploymentDefinition, len(servicesIdOrdering))
for i, id := range servicesIdOrdering {
serviceName := idToServiceName[id]
servicesOrdering[i] = services[serviceName].DeploymentDefinition
}

return servicesOrdering, nil
}

func (h *KoyebComposeHandler) MonitorService(ctx *CLIContext, deploymentId, serviceName string) error {
Expand Down Expand Up @@ -154,7 +221,10 @@ func (h *KoyebComposeHandler) MonitorService(ctx *CLIContext, deploymentId, serv
log.Infof("\nSucccessfully deployed %v ✅", serviceName)
} else {
log.Errorf("\nFailed to deploy %v deployment status: %v ❌", serviceName, previousStatus)
return fmt.Errorf("failed to deploy %v", serviceName)
return &errors.CLIError{
What: fmt.Sprintf("failed to deploy %v", serviceName),
Additional: []string{"please double check koyeb compose definition"},
}
}

return nil
Expand Down

0 comments on commit db2f559

Please sign in to comment.