@@ -18,6 +18,7 @@ package ecs
18
18
19
19
import (
20
20
"fmt"
21
+ "math"
21
22
"strconv"
22
23
23
24
"github.com/compose-spec/compose-go/types"
@@ -74,69 +75,138 @@ func (f family) firstOrError(msg string, args ...interface{}) (machine, error) {
74
75
}
75
76
76
77
func guessMachineType (project * types.Project ) (string , error ) {
77
- // we select a machine type to match all gpu -bound services requirements
78
+ // we select a machine type to match all gpus -bound services requirements
78
79
// once https://github.com/aws/containers-roadmap/issues/631 is implemented we can define dedicated CapacityProviders per service.
79
- minMemory , minCPU , minGPU , err := getResourceRequirements (project )
80
+ requirements , err := getResourceRequirements (project )
80
81
if err != nil {
81
82
return "" , err
82
83
}
83
84
84
85
instanceType , err := p3family .
85
86
filter (func (m machine ) bool {
86
- return m .memory >= minMemory
87
+ return m .memory >= requirements . memory
87
88
}).
88
89
filter (func (m machine ) bool {
89
- return m .cpus >= minCPU
90
+ return m .cpus >= requirements . cpus
90
91
}).
91
92
filter (func (m machine ) bool {
92
- return m .gpus >= minGPU
93
+ return m .gpus >= requirements . gpus
93
94
}).
94
- firstOrError ("none of the AWS p3 machines match requirement for memory:%d cpu:%f gpu :%d" , minMemory , minCPU , minGPU )
95
+ firstOrError ("none of the AWS p3 machines match requirement for memory:%d cpu:%f gpus :%d" , requirements . memory , requirements . cpus , requirements . gpus )
95
96
if err != nil {
96
97
return "" , err
97
98
}
98
99
return instanceType .id , nil
99
100
}
100
101
101
- func getResourceRequirements (project * types.Project ) (types.UnitBytes , float64 , int64 , error ) {
102
- var minMemory types.UnitBytes
103
- var minCPU float64
104
- var minGPU int64
102
+ type resourceRequirements struct {
103
+ memory types.UnitBytes
104
+ cpus float64
105
+ gpus int64
106
+ }
107
+
108
+ func getResourceRequirements (project * types.Project ) (* resourceRequirements , error ) {
109
+ return toResourceRequirementsSlice (project ).
110
+ filter (func (requirements * resourceRequirements ) bool {
111
+ return requirements .gpus != 0
112
+ }).
113
+ max ()
114
+ }
115
+
116
+ type eitherRequirementsOrError struct {
117
+ requirements []* resourceRequirements
118
+ err error
119
+ }
120
+
121
+ func toResourceRequirementsSlice (project * types.Project ) eitherRequirementsOrError {
122
+ var requirements []* resourceRequirements
105
123
for _ , service := range project .Services {
106
- if service .Deploy == nil {
107
- continue
108
- }
109
- reservations := service .Deploy .Resources .Reservations
110
- if reservations == nil {
111
- continue
124
+ r , err := toResourceRequirements (service )
125
+ if err != nil {
126
+ return eitherRequirementsOrError {nil , err }
112
127
}
128
+ requirements = append (requirements , r )
129
+ }
130
+ return eitherRequirementsOrError {requirements , nil }
131
+ }
113
132
114
- var requiredGPUs int64
115
- for _ , r := range reservations .GenericResources {
116
- if r .DiscreteResourceSpec .Kind == "gpu" {
117
- requiredGPUs = r .DiscreteResourceSpec .Value
118
- break
119
- }
120
- }
121
- if requiredGPUs == 0 {
122
- continue
123
- }
124
- if requiredGPUs > minGPU {
125
- minGPU = requiredGPUs
133
+ func (r eitherRequirementsOrError ) filter (fn func (* resourceRequirements ) bool ) eitherRequirementsOrError {
134
+ if r .err != nil {
135
+ return r
136
+ }
137
+ var requirements []* resourceRequirements
138
+ for _ , req := range r .requirements {
139
+ if fn (req ) {
140
+ requirements = append (requirements , req )
126
141
}
142
+ }
143
+ return eitherRequirementsOrError {requirements , nil }
144
+ }
127
145
128
- if reservations .MemoryBytes > minMemory {
129
- minMemory = reservations .MemoryBytes
146
+ func toResourceRequirements (service types.ServiceConfig ) (* resourceRequirements , error ) {
147
+ if service .Deploy == nil {
148
+ return nil , nil
149
+ }
150
+ reservations := service .Deploy .Resources .Reservations
151
+ if reservations == nil {
152
+ return nil , nil
153
+ }
154
+
155
+ var requiredGPUs int64
156
+ for _ , r := range reservations .GenericResources {
157
+ if r .DiscreteResourceSpec .Kind == "gpus" {
158
+ requiredGPUs = r .DiscreteResourceSpec .Value
159
+ break
130
160
}
131
- if reservations .NanoCPUs != "" {
132
- nanocpu , err := strconv .ParseFloat (reservations .NanoCPUs , 64 )
133
- if err != nil {
134
- return 0 , 0 , 0 , err
135
- }
136
- if nanocpu > minCPU {
137
- minCPU = nanocpu
138
- }
161
+ }
162
+
163
+ var nanocpu float64
164
+ if reservations .NanoCPUs != "" {
165
+ v , err := strconv .ParseFloat (reservations .NanoCPUs , 64 )
166
+ if err != nil {
167
+ return nil , err
139
168
}
169
+ nanocpu = v
170
+ }
171
+ return & resourceRequirements {
172
+ memory : reservations .MemoryBytes ,
173
+ cpus : nanocpu ,
174
+ gpus : requiredGPUs ,
175
+ }, nil
176
+ }
177
+
178
+ func (r resourceRequirements ) combine (o * resourceRequirements ) resourceRequirements {
179
+ if o == nil {
180
+ return r
181
+ }
182
+ return resourceRequirements {
183
+ memory : maxUnitBytes (r .memory , o .memory ),
184
+ cpus : math .Max (r .cpus , o .cpus ),
185
+ gpus : maxInt64 (r .gpus , o .gpus ),
186
+ }
187
+ }
188
+
189
+ func (r eitherRequirementsOrError ) max () (* resourceRequirements , error ) {
190
+ if r .err != nil {
191
+ return nil , r .err
192
+ }
193
+ min := resourceRequirements {}
194
+ for _ , req := range r .requirements {
195
+ min = min .combine (req )
196
+ }
197
+ return & min , nil
198
+ }
199
+
200
+ func maxInt64 (a , b int64 ) int64 {
201
+ if a > b {
202
+ return a
203
+ }
204
+ return b
205
+ }
206
+
207
+ func maxUnitBytes (a , b types.UnitBytes ) types.UnitBytes {
208
+ if a > b {
209
+ return a
140
210
}
141
- return minMemory , minCPU , minGPU , nil
211
+ return b
142
212
}
0 commit comments