Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5a436ca
updated kind with apisplitter
tthebst Jun 29, 2020
a15751a
added apisplitter useconfig
tthebst Jun 29, 2020
076f817
apisplit yaml example
tthebst Jun 29, 2020
71ed5a4
created apisplitter resource barebones
tthebst Jun 29, 2020
96fb362
added apisplitter validations
tthebst Jun 29, 2020
3de9271
migrated logic to overloading api config
tthebst Jun 30, 2020
1f2b225
basic post spec validation done
tthebst Jul 2, 2020
2a66b27
adding splitting logic to virtual service
tthebst Jul 2, 2020
bd5159a
working splitter update
tthebst Jul 3, 2020
840ac1b
api gateway support for trafficsplit
tthebst Jul 3, 2020
bee84df
delete useless stuff for trafficsplitter
tthebst Jul 3, 2020
a0dc90f
merge master into branch && resolv conflict
tthebst Jul 3, 2020
e32770d
add get command for trafficsplitter
tthebst Jul 4, 2020
7f3203f
add trafficsplit table and get all deployments
tthebst Jul 4, 2020
6c8bda0
cleanup and comments
tthebst Jul 6, 2020
f61d9a5
cli update to get status of trafficsplitter
tthebst Jul 6, 2020
d2ece7d
fix test error
tthebst Jul 6, 2020
b7f0ef6
fix comparison on virtual services
tthebst Jul 6, 2020
c8b3aa3
new traffic split get table
tthebst Jul 6, 2020
42dcf9b
add new get all table for trafficsplitter
tthebst Jul 6, 2020
e428df3
add endpoint duplication check
tthebst Jul 6, 2020
3aa441e
add check if api used by apisplitter
tthebst Jul 8, 2020
528e143
fix bug virtual service naming and remove print statements
tthebst Jul 8, 2020
764a22e
apisplitter not supported on local check
tthebst Jul 8, 2020
caba5a9
imporve cli output format
tthebst Jul 8, 2020
b41d640
fixes and formatting
tthebst Jul 8, 2020
699534f
allow 0 weight trafficsplitter
tthebst Jul 8, 2020
fbc6f1d
Merge branch 'master' into trafficsplit
tthebst Jul 9, 2020
982e2c6
update cli trafficsplit get table
tthebst Jul 11, 2020
6effac9
improve error messages
tthebst Jul 11, 2020
80ce487
Merge branch 'trafficsplit' of github.com:cortexlabs/cortex into traf…
tthebst Jul 11, 2020
4180e84
Merge branch 'master' into trafficsplit
vishalbollu Jul 14, 2020
49a909d
Update apisplit.yaml
vishalbollu Jul 14, 2020
d22e9d8
rewrite virtual service destination definition
tthebst Jul 14, 2020
3bc5ab4
code review improvments
tthebst Jul 15, 2020
cdbeb86
review improvements
tthebst Jul 16, 2020
dc9c7bb
code review improvements
tthebst Jul 22, 2020
5e2bc69
remove apisplit example
tthebst Jul 22, 2020
d784c5a
correct table for cortex get
tthebst Jul 22, 2020
493c1d1
remove print statements
tthebst Jul 22, 2020
7dcf104
code review improvements
tthebst Jul 26, 2020
b414dda
add API uniqueness check && review improvements
tthebst Jul 28, 2020
3f0afe5
Nits
vishalbollu Jul 29, 2020
b8b8ba8
Only allow logs command for Sync APIs
vishalbollu Jul 29, 2020
da67d06
Merge branch 'master' into trafficsplit
vishalbollu Jul 29, 2020
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
review improvements
  • Loading branch information
tthebst committed Jul 16, 2020
commit cdbeb8618a079b59dfb3b9832f12a6065cd5d2e0
12 changes: 6 additions & 6 deletions cli/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,10 @@ func apiSplitterTable(apiSplitter *schema.APISplitter, env cliconfig.Environment
return out, nil
}

func trafficSplitTable(trafficSplitter schema.APISplitter, env cliconfig.Environment) (table.Table, error) {
rows := make([][]interface{}, 0, len(trafficSplitter.Spec.APIs))
func trafficSplitTable(apiSplitter schema.APISplitter, env cliconfig.Environment) (table.Table, error) {
rows := make([][]interface{}, 0, len(apiSplitter.Spec.APIs))

for _, api := range trafficSplitter.Spec.APIs {
for _, api := range apiSplitter.Spec.APIs {
apiRes, err := cluster.GetAPI(MustGetOperatorConfig(env.Name), api.Name)
if err != nil {
return table.Table{}, err
Expand Down Expand Up @@ -392,10 +392,10 @@ func trafficSplitTable(trafficSplitter schema.APISplitter, env cliconfig.Environ
}, nil
}

func apiSplitterListTable(trafficSplitter []schema.APISplitter, envNames []string) table.Table {
rows := make([][]interface{}, 0, len(trafficSplitter))
func apiSplitterListTable(apiSplitter []schema.APISplitter, envNames []string) table.Table {
rows := make([][]interface{}, 0, len(apiSplitter))

for i, splitAPI := range trafficSplitter {
for i, splitAPI := range apiSplitter {
lastUpdated := time.Unix(splitAPI.Spec.LastUpdated, 0)
var apis []string
for _, api := range splitAPI.Spec.APIs {
Expand Down
12 changes: 6 additions & 6 deletions pkg/operator/resources/apisplitter/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func UpdateAPI(apiConfig *userconfig.API, projectID string, force bool) (*spec.A
return api, fmt.Sprintf("created %s", api.Name), nil
}

if !areVirtualServiceEqual(prevVirtualService, virtualServiceSpec(api, getTrafficSplitterDestinations(api))) {
if !areVirtualServiceEqual(prevVirtualService, virtualServiceSpec(api)) {
if err := config.AWS.UploadMsgpackToS3(api, config.Cluster.Bucket, api.Key); err != nil {
return nil, "", errors.Wrap(err, "upload api spec")
}
Expand Down Expand Up @@ -119,8 +119,8 @@ func applyK8sResources(api *spec.API, prevVirtualService *istioclientnetworking.
return applyK8sVirtualService(api, prevVirtualService)
}

func applyK8sVirtualService(trafficsplitter *spec.API, prevVirtualService *istioclientnetworking.VirtualService) error {
newVirtualService := virtualServiceSpec(trafficsplitter, getTrafficSplitterDestinations(trafficsplitter))
func applyK8sVirtualService(apiSplitter *spec.API, prevVirtualService *istioclientnetworking.VirtualService) error {
newVirtualService := virtualServiceSpec(apiSplitter)

if prevVirtualService == nil {
_, err := config.K8s.CreateVirtualService(newVirtualService)
Expand All @@ -131,9 +131,9 @@ func applyK8sVirtualService(trafficsplitter *spec.API, prevVirtualService *istio
return err
}

func getTrafficSplitterDestinations(trafficsplitter *spec.API) []k8s.Destination {
destinations := make([]k8s.Destination, len(trafficsplitter.APIs))
for i, api := range trafficsplitter.APIs {
func getAPISplitterDestinations(apiSplitter *spec.API) []k8s.Destination {
destinations := make([]k8s.Destination, len(apiSplitter.APIs))
for i, api := range apiSplitter.APIs {
destinations[i] = k8s.Destination{
ServiceName: operator.K8sName(api.Name),
Weight: int32(api.Weight),
Expand Down
18 changes: 9 additions & 9 deletions pkg/operator/resources/apisplitter/k8s_specs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ const (
_defaultPortInt32, _defaultPortStr = int32(8888), "8888"
)

func virtualServiceSpec(trafficsplitter *spec.API, destinations []k8s.Destination) *istioclientnetworking.VirtualService {
func virtualServiceSpec(apiSplitter *spec.API) *istioclientnetworking.VirtualService {
return k8s.VirtualService(&k8s.VirtualServiceSpec{
Name: operator.K8sName(trafficsplitter.Name),
Name: operator.K8sName(apiSplitter.Name),
Gateways: []string{"apis-gateway"},
Destinations: destinations,
Path: *trafficsplitter.Networking.Endpoint,
Destinations: getAPISplitterDestinations(apiSplitter),
Path: *apiSplitter.Networking.Endpoint,
Rewrite: pointer.String("predict"),
Annotations: map[string]string{
userconfig.EndpointAnnotationKey: *trafficsplitter.Networking.Endpoint,
userconfig.APIGatewayAnnotationKey: trafficsplitter.Networking.APIGateway.String()},
userconfig.EndpointAnnotationKey: *apiSplitter.Networking.Endpoint,
userconfig.APIGatewayAnnotationKey: apiSplitter.Networking.APIGateway.String()},
Labels: map[string]string{
"apiName": trafficsplitter.Name,
"apiKind": trafficsplitter.Kind.String(),
"apiID": trafficsplitter.ID,
"apiName": apiSplitter.Name,
"apiKind": apiSplitter.Kind.String(),
"apiID": apiSplitter.ID,
},
})
}
8 changes: 4 additions & 4 deletions pkg/operator/resources/apisplitter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func GetStatus(apiName string) (*status.Status, error) {
return nil, errors.ErrorUnexpected("unable to find trafficsplitter", apiName)
}

return trafficSplitterStatus(virtualService)
return apiSplitterStatus(virtualService)
}

func GetAllStatuses() ([]status.Status, error) {
Expand All @@ -50,7 +50,7 @@ func GetAllStatuses() ([]status.Status, error) {

statuses := make([]status.Status, len(virtualServices))
for i, virtualService := range virtualServices {
status, err := trafficSplitterStatus(&virtualService)
status, err := apiSplitterStatus(&virtualService)
if err != nil {
return nil, err
}
Expand All @@ -63,12 +63,12 @@ func GetAllStatuses() ([]status.Status, error) {
return statuses, nil
}

func trafficSplitterStatus(virtualService *istioclientnetworking.VirtualService) (*status.Status, error) {
func apiSplitterStatus(virtualService *istioclientnetworking.VirtualService) (*status.Status, error) {

statusResponse := &status.Status{}
statusResponse.APIName = virtualService.Labels["apiName"]
statusResponse.APIID = virtualService.Labels["apiID"]
// if virtual service deploy the trafficsplitter is actice
// if virtual service deploy the trafficsplitter is active
// maybe need to check if backends are active
statusResponse.Code = status.Live

Expand Down
4 changes: 2 additions & 2 deletions pkg/operator/resources/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const (
ErrCannotChangeTypeOfDeployedAPI = "resources.cannot_change_kind_of_deployed_api"
ErrNoAvailableNodeComputeLimit = "resources.no_available_node_compute_limit"
ErrAPIUsedByAPISplitter = "resources.syncapi_used_by_apisplitter"
ErrNotDeployedAPIsAPISplitter = "resources.trafficsplit_apis_not_deplyoed"
ErrNotDeployedAPIsAPISplitter = "resources.trafficsplit_apis_not_deployed"
)

func ErrorOperationNotSupportedForKind(kind userconfig.Kind) error {
Expand Down Expand Up @@ -68,7 +68,7 @@ func ErrorNoAvailableNodeComputeLimit(resource string, reqStr string, maxStr str
func ErrorAPIUsedByAPISplitter(apiSplitters []string) error {
return errors.WithStack(&errors.Error{
Kind: ErrAPIUsedByAPISplitter,
Message: fmt.Sprintf("can not delete api because it is used by APISplitter: %s", strings.StrsSentence(apiSplitters, "")),
Message: fmt.Sprintf("cannot delete api because it is used by the following %s: %s", strings.PluralS("APISplitter", len(apiSplitters)), strings.StrsSentence(apiSplitters, "")),
})
}

Expand Down
42 changes: 24 additions & 18 deletions pkg/operator/resources/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,26 @@ func Deploy(projectBytes []byte, configFileName string, configBytes []byte, forc
ConfigFileName: configFileName,
}

isProjectUploaded, err := config.AWS.IsS3File(config.Cluster.Bucket, projectKey)
apiConfigs, err := spec.ExtractAPIConfigs(configBytes, types.AWSProviderType, configFileName)
if err != nil {
return nil, err
}
if !isProjectUploaded {
if err = config.AWS.UploadBytesToS3(projectBytes, config.Cluster.Bucket, projectKey); err != nil {
return nil, err
}
}

apiConfigs, err := spec.ExtractAPIConfigs(configBytes, types.AWSProviderType, configFileName)
err = ValidateClusterAPIs(apiConfigs, projectFiles)
if err != nil {
err = errors.Append(err, fmt.Sprintf("\n\napi configuration schema can be found here: https://docs.cortex.dev/v/%s/deployments/api-configuration", consts.CortexVersionMinor))
return nil, err
}

err = ValidateClusterAPIs(apiConfigs, projectFiles)
isProjectUploaded, err := config.AWS.IsS3File(config.Cluster.Bucket, projectKey)
if err != nil {
err = errors.Append(err, fmt.Sprintf("\n\napi configuration schema can be found here: https://docs.cortex.dev/v/%s/deployments/api-configuration", consts.CortexVersionMinor))
return nil, err
}
if !isProjectUploaded {
if err = config.AWS.UploadBytesToS3(projectBytes, config.Cluster.Bucket, projectKey); err != nil {
return nil, err
}
}

// order apiconfigs first syncAPIs then TrafficSplit
// This is done if user specifies SyncAPIs in same file as APISplitter
Expand Down Expand Up @@ -247,17 +247,17 @@ func GetAPIs() (*schema.GetAPIsResponse, error) {
}

// get all apiSplitters
apiSplitterstatuses, err := apisplitter.GetAllStatuses()
apiSplitterStatuses, err := apisplitter.GetAllStatuses()
if err != nil {
return nil, err
}
if len(apiSplitterstatuses) > 0 {
apiSplitterapiNames, apiSplitterapiIDs := namesAndIDsFromStatuses(apiSplitterstatuses)
apiSplitterapis, err := operator.DownloadAPISpecs(apiSplitterapiNames, apiSplitterapiIDs)
if len(apiSplitterStatuses) > 0 {
apiSplitterAPINames, apiSplitterAPIIDs := namesAndIDsFromStatuses(apiSplitterStatuses)
apiSplitterAPIs, err := operator.DownloadAPISpecs(apiSplitterAPINames, apiSplitterAPIIDs)
if err != nil {
return nil, err
}
for _, api := range apiSplitterapis {
for _, api := range apiSplitterAPIs {
apiSplitter = append(apiSplitter, schema.APISplitter{
Spec: api,
})
Expand Down Expand Up @@ -340,16 +340,22 @@ func namesAndIDsFromStatuses(statuses []status.Status) ([]string, []string) {
return apiNames, apiIDs
}

//checkIfUsedByAPISplitter checks if api is used by a deployed APISplitter
func checkIfUsedByAPISplitter(apiName string) error {
apiRes, err := GetAPIs()
virtualServices, err := config.K8s.ListVirtualServicesByLabel("apiKind", userconfig.APISplitterKind.String())
if err != nil {
return err
}

var usedByAPISplitters []string
for _, apiSplitter := range apiRes.APISplitter {
for _, api := range apiSplitter.Spec.APIs {
for _, vs := range virtualServices {
apiSplitterSpec, err := operator.DownloadAPISpec(vs.Labels["apiName"], vs.Labels["apiID"])
if err != nil {
return err
}
for _, api := range apiSplitterSpec.APIs {
if apiName == api.Name {
usedByAPISplitters = append(usedByAPISplitters, apiSplitter.Spec.Name)
usedByAPISplitters = append(usedByAPISplitters, apiSplitterSpec.Name)
}
}
}
Expand Down
7 changes: 1 addition & 6 deletions pkg/operator/resources/validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,7 @@ func findDuplicateEndpoints(apis []userconfig.API) []userconfig.API {
endpoints := make(map[string][]userconfig.API)

for _, api := range apis {
if api.Kind == userconfig.SyncAPIKind {
endpoints[*api.Networking.Endpoint] = append(endpoints[*api.Networking.Endpoint], api)
}
if api.Kind == userconfig.APISplitterKind {
endpoints[*api.Networking.Endpoint] = append(endpoints[*api.Networking.Endpoint], api)
}
endpoints[*api.Networking.Endpoint] = append(endpoints[*api.Networking.Endpoint], api)
}

for endpoint := range endpoints {
Expand Down
2 changes: 1 addition & 1 deletion pkg/operator/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type DeployResult struct {

type GetAPIsResponse struct {
SyncAPIs []SyncAPI `json:"sync_apis"`
APISplitter []APISplitter `json:"api_splitter"`
APISplitter []APISplitter `json:"api_splitters"`
}

type SyncAPI struct {
Expand Down
88 changes: 32 additions & 56 deletions pkg/types/spec/validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func apiValidation(provider types.ProviderType, resource userconfig.Resource) *c
if resource.Kind == userconfig.SyncAPIKind {
structFieldValidations = append(structFieldValidations,
predictorValidation(),
networkingValidation(),
networkingValidation(resource.Kind),
computeValidation(provider),
monitoringValidation(),
autoscalingValidation(provider),
Expand All @@ -64,7 +64,7 @@ func apiValidation(provider types.ProviderType, resource userconfig.Resource) *c
if resource.Kind == userconfig.APISplitterKind {
structFieldValidations = append(structFieldValidations,
multiAPIsValidation(),
networkingValidationNoLocal(),
networkingValidation(resource.Kind),
)
}
return &cr.StructValidation{
Expand Down Expand Up @@ -122,33 +122,6 @@ func multiAPIsValidation() *cr.StructFieldValidation {
}
}

func networkingValidationNoLocal() *cr.StructFieldValidation {
return &cr.StructFieldValidation{
StructField: "Networking",
StructValidation: &cr.StructValidation{
StructFieldValidations: []*cr.StructFieldValidation{
{
StructField: "Endpoint",
StringPtrValidation: &cr.StringPtrValidation{
Validator: urls.ValidateEndpoint,
MaxLength: 1000, // no particular reason other than it works
},
},
{
StructField: "APIGateway",
StringValidation: &cr.StringValidation{
AllowedValues: userconfig.APIGatewayTypeStrings(),
Default: userconfig.PublicAPIGatewayType.String(),
},
Parser: func(str string) (interface{}, error) {
return userconfig.APIGatewayTypeFromString(str), nil
},
},
},
},
}
}

func predictorValidation() *cr.StructFieldValidation {
return &cr.StructFieldValidation{
StructField: "Predictor",
Expand Down Expand Up @@ -275,36 +248,39 @@ func monitoringValidation() *cr.StructFieldValidation {
}
}

func networkingValidation() *cr.StructFieldValidation {
func networkingValidation(kind userconfig.Kind) *cr.StructFieldValidation {
structFieldValidation := []*cr.StructFieldValidation{
{
StructField: "Endpoint",
StringPtrValidation: &cr.StringPtrValidation{
Validator: urls.ValidateEndpoint,
MaxLength: 1000, // no particular reason other than it works
},
},
{
StructField: "APIGateway",
StringValidation: &cr.StringValidation{
AllowedValues: userconfig.APIGatewayTypeStrings(),
Default: userconfig.PublicAPIGatewayType.String(),
},
Parser: func(str string) (interface{}, error) {
return userconfig.APIGatewayTypeFromString(str), nil
},
},
}
if kind == userconfig.SyncAPIKind {
structFieldValidation = append(structFieldValidation, &cr.StructFieldValidation{
StructField: "LocalPort",
IntPtrValidation: &cr.IntPtrValidation{
GreaterThan: pointer.Int(0),
LessThanOrEqualTo: pointer.Int(math.MaxUint16),
},
})
}
return &cr.StructFieldValidation{
StructField: "Networking",
StructValidation: &cr.StructValidation{
StructFieldValidations: []*cr.StructFieldValidation{
{
StructField: "Endpoint",
StringPtrValidation: &cr.StringPtrValidation{
Validator: urls.ValidateEndpoint,
MaxLength: 1000, // no particular reason other than it works
},
},
{
StructField: "LocalPort",
IntPtrValidation: &cr.IntPtrValidation{
GreaterThan: pointer.Int(0),
LessThanOrEqualTo: pointer.Int(math.MaxUint16),
},
},
{
StructField: "APIGateway",
StringValidation: &cr.StringValidation{
AllowedValues: userconfig.APIGatewayTypeStrings(),
Default: userconfig.PublicAPIGatewayType.String(),
},
Parser: func(str string) (interface{}, error) {
return userconfig.APIGatewayTypeFromString(str), nil
},
},
},
StructFieldValidations: structFieldValidation,
},
}
}
Expand Down