From 7d79152d8f0d4652aa8ef427be519986e39565c0 Mon Sep 17 00:00:00 2001 From: Abhijeet V <31417623+abvaidya@users.noreply.github.com> Date: Mon, 20 Nov 2023 08:01:06 -0800 Subject: [PATCH] new msd api to create transport policy (#2422) Signed-off-by: Abhijeet V <31417623+abvaidya@users.noreply.github.com> --- clients/go/msd/client.go | 38 ++++ clients/go/msd/model.go | 175 ++++++++++++++++++ clients/go/msd/msd_schema.go | 39 +++- .../athenz/msd/MSDRDLGeneratedClient.java | 35 ++++ .../java/com/yahoo/athenz/msd/MSDSchema.java | 41 +++- .../athenz/msd/TransportPolicyRequest.java | 109 +++++++++++ ...sportPolicySubjectSelectorRequirement.java | 61 ++++++ .../src/main/rdl/KubernetesNetworkPolicy.rdli | 2 +- .../msd/src/main/rdl/TransportPolicyRule.rdli | 20 +- core/msd/src/main/rdl/TransportPolicyRule.tdl | 18 ++ .../msd/TransportPolicyRequestTest.java | 84 +++++++++ ...tPolicySubjectSelectorRequirementTest.java | 53 ++++++ 12 files changed, 666 insertions(+), 9 deletions(-) create mode 100644 core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicyRequest.java create mode 100644 core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirement.java create mode 100644 core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicyRequestTest.java create mode 100644 core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirementTest.java diff --git a/clients/go/msd/client.go b/clients/go/msd/client.go index 5bce82d6c63..a4da3466164 100644 --- a/clients/go/msd/client.go +++ b/clients/go/msd/client.go @@ -435,6 +435,44 @@ func (client MSDClient) GetTransportPolicyRulesByDomain(domainName DomainName, m } } +func (client MSDClient) PutTransportPolicy(domainName DomainName, serviceName EntityName, payload *TransportPolicyRequest) (*TransportPolicyRules, error) { + var data *TransportPolicyRules + url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/transportpolicy" + contentBytes, err := json.Marshal(payload) + if err != nil { + return data, err + } + resp, err := client.httpPut(url, nil, contentBytes) + if err != nil { + return data, err + } + defer resp.Body.Close() + switch resp.StatusCode { + case 204, 200: + if 204 != resp.StatusCode { + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return data, err + } + } + return data, nil + default: + var errobj rdl.ResourceError + contentBytes, err = io.ReadAll(resp.Body) + if err != nil { + return data, err + } + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return data, errobj + } +} + func (client MSDClient) GetWorkloadsByService(domainName DomainName, serviceName EntityName, matchingTag string) (*Workloads, string, error) { var data *Workloads headers := map[string]string{ diff --git a/clients/go/msd/model.go b/clients/go/msd/model.go index e3f3a45cad7..5c1581af46e 100644 --- a/clients/go/msd/model.go +++ b/clients/go/msd/model.go @@ -1189,6 +1189,181 @@ func (self *TransportPolicyValidationResponseList) Validate() error { return nil } +// TransportPolicySubjectSelectorRequirement - A subject selector requirement +// is a selector that contains value, a key, and an operator that relates the +// key and value. +type TransportPolicySubjectSelectorRequirement struct { + + // + // key that the selector applies to + // + Key string `json:"key"` + + // + // Operator that is applied to the key and value + // + Operator string `json:"operator"` + + // + // Value that the selector applies to + // + Value string `json:"value"` +} + +// NewTransportPolicySubjectSelectorRequirement - creates an initialized TransportPolicySubjectSelectorRequirement instance, returns a pointer to it +func NewTransportPolicySubjectSelectorRequirement(init ...*TransportPolicySubjectSelectorRequirement) *TransportPolicySubjectSelectorRequirement { + var o *TransportPolicySubjectSelectorRequirement + if len(init) == 1 { + o = init[0] + } else { + o = new(TransportPolicySubjectSelectorRequirement) + } + return o +} + +type rawTransportPolicySubjectSelectorRequirement TransportPolicySubjectSelectorRequirement + +// UnmarshalJSON is defined for proper JSON decoding of a TransportPolicySubjectSelectorRequirement +func (self *TransportPolicySubjectSelectorRequirement) UnmarshalJSON(b []byte) error { + var m rawTransportPolicySubjectSelectorRequirement + err := json.Unmarshal(b, &m) + if err == nil { + o := TransportPolicySubjectSelectorRequirement(m) + *self = o + err = self.Validate() + } + return err +} + +// Validate - checks for missing required fields, etc +func (self *TransportPolicySubjectSelectorRequirement) Validate() error { + if self.Key == "" { + return fmt.Errorf("TransportPolicySubjectSelectorRequirement.key is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "String", self.Key) + if !val.Valid { + return fmt.Errorf("TransportPolicySubjectSelectorRequirement.key does not contain a valid String (%v)", val.Error) + } + } + if self.Operator == "" { + return fmt.Errorf("TransportPolicySubjectSelectorRequirement.operator is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "String", self.Operator) + if !val.Valid { + return fmt.Errorf("TransportPolicySubjectSelectorRequirement.operator does not contain a valid String (%v)", val.Error) + } + } + if self.Value == "" { + return fmt.Errorf("TransportPolicySubjectSelectorRequirement.value is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "String", self.Value) + if !val.Valid { + return fmt.Errorf("TransportPolicySubjectSelectorRequirement.value does not contain a valid String (%v)", val.Error) + } + } + return nil +} + +// TransportPolicyRequest - Input to create a transport policy +type TransportPolicyRequest struct { + + // + // Direction of network traffic + // + Direction TransportPolicyTrafficDirection `json:"direction"` + + // + // Policy Identifier + // + Identifier EntityName `json:"identifier"` + + // + // Subject for the policy + // + Subject *TransportPolicySubject `json:"subject"` + + // + // List of subject selector conditions + // + Conditions []*TransportPolicySubjectSelectorRequirement `json:"conditions,omitempty" rdl:"optional" yaml:",omitempty"` + + // + // List of source network traffic ports + // + SourcePorts []*TransportPolicyPort `json:"sourcePorts"` + + // + // List of destination network traffic ports + // + DestinationPorts []*TransportPolicyPort `json:"destinationPorts"` + + // + // Source or destination of the policy depending on direction + // + Peers []*TransportPolicySubject `json:"peers,omitempty" rdl:"optional" yaml:",omitempty"` +} + +// NewTransportPolicyRequest - creates an initialized TransportPolicyRequest instance, returns a pointer to it +func NewTransportPolicyRequest(init ...*TransportPolicyRequest) *TransportPolicyRequest { + var o *TransportPolicyRequest + if len(init) == 1 { + o = init[0] + } else { + o = new(TransportPolicyRequest) + } + return o.Init() +} + +// Init - sets up the instance according to its default field values, if any +func (self *TransportPolicyRequest) Init() *TransportPolicyRequest { + if self.Subject == nil { + self.Subject = NewTransportPolicySubject() + } + if self.SourcePorts == nil { + self.SourcePorts = make([]*TransportPolicyPort, 0) + } + if self.DestinationPorts == nil { + self.DestinationPorts = make([]*TransportPolicyPort, 0) + } + return self +} + +type rawTransportPolicyRequest TransportPolicyRequest + +// UnmarshalJSON is defined for proper JSON decoding of a TransportPolicyRequest +func (self *TransportPolicyRequest) UnmarshalJSON(b []byte) error { + var m rawTransportPolicyRequest + err := json.Unmarshal(b, &m) + if err == nil { + o := TransportPolicyRequest(m) + *self = *((&o).Init()) + err = self.Validate() + } + return err +} + +// Validate - checks for missing required fields, etc +func (self *TransportPolicyRequest) Validate() error { + if self.Identifier == "" { + return fmt.Errorf("TransportPolicyRequest.identifier is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "EntityName", self.Identifier) + if !val.Valid { + return fmt.Errorf("TransportPolicyRequest.identifier does not contain a valid EntityName (%v)", val.Error) + } + } + if self.Subject == nil { + return fmt.Errorf("TransportPolicyRequest: Missing required field: subject") + } + if self.SourcePorts == nil { + return fmt.Errorf("TransportPolicyRequest: Missing required field: sourcePorts") + } + if self.DestinationPorts == nil { + return fmt.Errorf("TransportPolicyRequest: Missing required field: destinationPorts") + } + return nil +} + // StaticWorkloadType - Enum representing defined types of static workloads. type StaticWorkloadType int diff --git a/clients/go/msd/msd_schema.go b/clients/go/msd/msd_schema.go index 0bfacc284fd..c6b0d8ca18f 100644 --- a/clients/go/msd/msd_schema.go +++ b/clients/go/msd/msd_schema.go @@ -216,6 +216,24 @@ func init() { tTransportPolicyValidationResponseList.ArrayField("responseList", "TransportPolicyValidationResponse", false, "list of transport policy validation response") sb.AddType(tTransportPolicyValidationResponseList.Build()) + tTransportPolicySubjectSelectorRequirement := rdl.NewStructTypeBuilder("Struct", "TransportPolicySubjectSelectorRequirement") + tTransportPolicySubjectSelectorRequirement.Comment("A subject selector requirement is a selector that contains value, a key, and an operator that relates the key and value.") + tTransportPolicySubjectSelectorRequirement.Field("key", "String", false, nil, "key that the selector applies to") + tTransportPolicySubjectSelectorRequirement.Field("operator", "String", false, nil, "Operator that is applied to the key and value") + tTransportPolicySubjectSelectorRequirement.Field("value", "String", false, nil, "Value that the selector applies to") + sb.AddType(tTransportPolicySubjectSelectorRequirement.Build()) + + tTransportPolicyRequest := rdl.NewStructTypeBuilder("Struct", "TransportPolicyRequest") + tTransportPolicyRequest.Comment("Input to create a transport policy") + tTransportPolicyRequest.Field("direction", "TransportPolicyTrafficDirection", false, nil, "Direction of network traffic") + tTransportPolicyRequest.Field("identifier", "EntityName", false, nil, "Policy Identifier") + tTransportPolicyRequest.Field("subject", "TransportPolicySubject", false, nil, "Subject for the policy") + tTransportPolicyRequest.ArrayField("conditions", "TransportPolicySubjectSelectorRequirement", true, "List of subject selector conditions") + tTransportPolicyRequest.ArrayField("sourcePorts", "TransportPolicyPort", false, "List of source network traffic ports") + tTransportPolicyRequest.ArrayField("destinationPorts", "TransportPolicyPort", false, "List of destination network traffic ports") + tTransportPolicyRequest.ArrayField("peers", "TransportPolicySubject", true, "Source or destination of the policy depending on direction") + sb.AddType(tTransportPolicyRequest.Build()) + tStaticWorkloadType := rdl.NewEnumTypeBuilder("Enum", "StaticWorkloadType") tStaticWorkloadType.Comment("Enum representing defined types of static workloads.") tStaticWorkloadType.Element("VIP", "represents a virtual ip construct") @@ -647,9 +665,9 @@ func init() { sb.AddResource(mGetTransportPolicyRules.Build()) mValidateTransportPolicy := rdl.NewResourceBuilder("TransportPolicyValidationResponse", "POST", "/transportpolicy/validate") - mValidateTransportPolicy.Comment("API to validate microsegmentation policies against network policies") + mValidateTransportPolicy.Comment("API to validate micro-segmentation policies against network policies") mValidateTransportPolicy.Name("validateTransportPolicy") - mValidateTransportPolicy.Input("transportPolicy", "TransportPolicyValidationRequest", false, "", "", false, nil, "Struct representing microsegmentation policy entered by the user") + mValidateTransportPolicy.Input("transportPolicy", "TransportPolicyValidationRequest", false, "", "", false, nil, "Struct representing micro-segmentation policy entered by the user") mValidateTransportPolicy.Auth("", "", true, "") mValidateTransportPolicy.Exception("BAD_REQUEST", "ResourceError", "") mValidateTransportPolicy.Exception("FORBIDDEN", "ResourceError", "") @@ -684,6 +702,21 @@ func init() { mGetTransportPolicyRulesByDomain.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(mGetTransportPolicyRulesByDomain.Build()) + mPutTransportPolicy := rdl.NewResourceBuilder("TransportPolicyRules", "PUT", "/domain/{domainName}/service/{serviceName}/transportpolicy") + mPutTransportPolicy.Comment("API endpoint to create a transport policy for a given domain and service") + mPutTransportPolicy.Name("putTransportPolicy") + mPutTransportPolicy.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") + mPutTransportPolicy.Input("serviceName", "EntityName", true, "", "", false, nil, "Name of the service") + mPutTransportPolicy.Input("payload", "TransportPolicyRequest", false, "", "", false, nil, "Struct representing input transport policy") + mPutTransportPolicy.Auth("msd.UpdateNetworkPolicy", "{domainName}:service.{serviceName}", false, "") + mPutTransportPolicy.Expected("NO_CONTENT") + mPutTransportPolicy.Exception("BAD_REQUEST", "ResourceError", "") + mPutTransportPolicy.Exception("FORBIDDEN", "ResourceError", "") + mPutTransportPolicy.Exception("NOT_FOUND", "ResourceError", "") + mPutTransportPolicy.Exception("TOO_MANY_REQUESTS", "ResourceError", "") + mPutTransportPolicy.Exception("UNAUTHORIZED", "ResourceError", "") + sb.AddResource(mPutTransportPolicy.Build()) + mGetWorkloadsByService := rdl.NewResourceBuilder("Workloads", "GET", "/domain/{domainName}/service/{serviceName}/workloads") mGetWorkloadsByService.Name("getWorkloadsByService") mGetWorkloadsByService.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") @@ -830,7 +863,7 @@ func init() { mPostKubernetesNetworkPolicyRequest.Input("request", "KubernetesNetworkPolicyRequest", false, "", "", false, nil, "Struct representing input options based on the cluster context") mPostKubernetesNetworkPolicyRequest.Input("matchingTag", "String", false, "", "If-None-Match", false, nil, "Retrieved from the previous request, this timestamp specifies to the server to return any policies modified since this time") mPostKubernetesNetworkPolicyRequest.Output("tag", "String", "ETag", false, "The current latest modification timestamp is returned in this header") - mPostKubernetesNetworkPolicyRequest.Auth("read", "{domainName}:service.{serviceName}", false, "") + mPostKubernetesNetworkPolicyRequest.Auth("msd.GetNetworkPolicy", "{domainName}:service.{serviceName}", false, "") mPostKubernetesNetworkPolicyRequest.Exception("BAD_REQUEST", "ResourceError", "") mPostKubernetesNetworkPolicyRequest.Exception("FORBIDDEN", "ResourceError", "") mPostKubernetesNetworkPolicyRequest.Exception("NOT_FOUND", "ResourceError", "") diff --git a/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java b/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java index cbdbfe7be4e..3a8c0d036dd 100644 --- a/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java +++ b/clients/java/msd/src/main/java/com/yahoo/athenz/msd/MSDRDLGeneratedClient.java @@ -251,6 +251,41 @@ public TransportPolicyRules getTransportPolicyRulesByDomain(String domainName, S } } + public TransportPolicyRules putTransportPolicy(String domainName, String serviceName, TransportPolicyRequest payload) throws URISyntaxException, IOException { + UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/service/{serviceName}/transportpolicy") + .resolveTemplate("domainName", domainName) + .resolveTemplate("serviceName", serviceName); + URIBuilder uriBuilder = new URIBuilder(uriTemplateBuilder.getUri()); + HttpEntity httpEntity = new StringEntity(jsonMapper.writeValueAsString(payload), ContentType.APPLICATION_JSON); + HttpUriRequest httpUriRequest = RequestBuilder.put() + .setUri(uriBuilder.build()) + .setEntity(httpEntity) + .build(); + if (credsHeader != null) { + httpUriRequest.addHeader(credsHeader, credsToken); + } + HttpEntity httpResponseEntity = null; + try (CloseableHttpResponse httpResponse = client.execute(httpUriRequest, httpContext)) { + int code = httpResponse.getStatusLine().getStatusCode(); + httpResponseEntity = httpResponse.getEntity(); + switch (code) { + case 204: + case 200: + if (code == 204) { + return null; + } + return jsonMapper.readValue(httpResponseEntity.getContent(), TransportPolicyRules.class); + default: + final String errorData = (httpResponseEntity == null) ? null : EntityUtils.toString(httpResponseEntity); + throw (errorData != null && !errorData.isEmpty()) + ? new ResourceException(code, jsonMapper.readValue(errorData, ResourceError.class)) + : new ResourceException(code); + } + } finally { + EntityUtils.consumeQuietly(httpResponseEntity); + } + } + public Workloads getWorkloadsByService(String domainName, String serviceName, String matchingTag, java.util.Map> headers) throws URISyntaxException, IOException { UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/service/{serviceName}/workloads") .resolveTemplate("domainName", domainName) diff --git a/core/msd/src/main/java/com/yahoo/athenz/msd/MSDSchema.java b/core/msd/src/main/java/com/yahoo/athenz/msd/MSDSchema.java index 1cb934b0d24..53540d0a915 100644 --- a/core/msd/src/main/java/com/yahoo/athenz/msd/MSDSchema.java +++ b/core/msd/src/main/java/com/yahoo/athenz/msd/MSDSchema.java @@ -183,6 +183,22 @@ private static Schema build() { .comment("List of TransportPolicyValidationResponse") .arrayField("responseList", "TransportPolicyValidationResponse", false, "list of transport policy validation response"); + sb.structType("TransportPolicySubjectSelectorRequirement") + .comment("A subject selector requirement is a selector that contains value, a key, and an operator that relates the key and value.") + .field("key", "String", false, "key that the selector applies to") + .field("operator", "String", false, "Operator that is applied to the key and value") + .field("value", "String", false, "Value that the selector applies to"); + + sb.structType("TransportPolicyRequest") + .comment("Input to create a transport policy") + .field("direction", "TransportPolicyTrafficDirection", false, "Direction of network traffic") + .field("identifier", "EntityName", false, "Policy Identifier") + .field("subject", "TransportPolicySubject", false, "Subject for the policy") + .arrayField("conditions", "TransportPolicySubjectSelectorRequirement", true, "List of subject selector conditions") + .arrayField("sourcePorts", "TransportPolicyPort", false, "List of source network traffic ports") + .arrayField("destinationPorts", "TransportPolicyPort", false, "List of destination network traffic ports") + .arrayField("peers", "TransportPolicySubject", true, "Source or destination of the policy depending on direction"); + sb.enumType("StaticWorkloadType") .comment("Enum representing defined types of static workloads.") .element("VIP") @@ -564,9 +580,9 @@ private static Schema build() { ; sb.resource("TransportPolicyValidationRequest", "POST", "/transportpolicy/validate") - .comment("API to validate microsegmentation policies against network policies") + .comment("API to validate micro-segmentation policies against network policies") .name("validateTransportPolicy") - .input("transportPolicy", "TransportPolicyValidationRequest", "Struct representing microsegmentation policy entered by the user") + .input("transportPolicy", "TransportPolicyValidationRequest", "Struct representing micro-segmentation policy entered by the user") .auth("", "", true) .expected("OK") .exception("BAD_REQUEST", "ResourceError", "") @@ -616,6 +632,25 @@ private static Schema build() { .exception("UNAUTHORIZED", "ResourceError", "") ; + sb.resource("TransportPolicyRequest", "PUT", "/domain/{domainName}/service/{serviceName}/transportpolicy") + .comment("API endpoint to create a transport policy for a given domain and service") + .name("putTransportPolicy") + .pathParam("domainName", "DomainName", "name of the domain") + .pathParam("serviceName", "EntityName", "Name of the service") + .input("payload", "TransportPolicyRequest", "Struct representing input transport policy") + .auth("msd.UpdateNetworkPolicy", "{domainName}:service.{serviceName}") + .expected("NO_CONTENT") + .exception("BAD_REQUEST", "ResourceError", "") + + .exception("FORBIDDEN", "ResourceError", "") + + .exception("NOT_FOUND", "ResourceError", "") + + .exception("TOO_MANY_REQUESTS", "ResourceError", "") + + .exception("UNAUTHORIZED", "ResourceError", "") +; + sb.resource("Workloads", "GET", "/domain/{domainName}/service/{serviceName}/workloads") .name("getWorkloadsByService") .pathParam("domainName", "DomainName", "name of the domain") @@ -808,7 +843,7 @@ private static Schema build() { .input("request", "KubernetesNetworkPolicyRequest", "Struct representing input options based on the cluster context") .headerParam("If-None-Match", "matchingTag", "String", null, "Retrieved from the previous request, this timestamp specifies to the server to return any policies modified since this time") .output("ETag", "tag", "String", "The current latest modification timestamp is returned in this header") - .auth("read", "{domainName}:service.{serviceName}") + .auth("msd.GetNetworkPolicy", "{domainName}:service.{serviceName}") .expected("OK") .exception("BAD_REQUEST", "ResourceError", "") diff --git a/core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicyRequest.java b/core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicyRequest.java new file mode 100644 index 00000000000..d885d0b3c6e --- /dev/null +++ b/core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicyRequest.java @@ -0,0 +1,109 @@ +// +// This file generated by rdl 1.5.2. Do not modify! +// + +package com.yahoo.athenz.msd; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; +import com.yahoo.rdl.*; + +// +// TransportPolicyRequest - Input to create a transport policy +// +@JsonIgnoreProperties(ignoreUnknown = true) +public class TransportPolicyRequest { + public TransportPolicyTrafficDirection direction; + public String identifier; + public TransportPolicySubject subject; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public List conditions; + public List sourcePorts; + public List destinationPorts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public List peers; + + public TransportPolicyRequest setDirection(TransportPolicyTrafficDirection direction) { + this.direction = direction; + return this; + } + public TransportPolicyTrafficDirection getDirection() { + return direction; + } + public TransportPolicyRequest setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + public String getIdentifier() { + return identifier; + } + public TransportPolicyRequest setSubject(TransportPolicySubject subject) { + this.subject = subject; + return this; + } + public TransportPolicySubject getSubject() { + return subject; + } + public TransportPolicyRequest setConditions(List conditions) { + this.conditions = conditions; + return this; + } + public List getConditions() { + return conditions; + } + public TransportPolicyRequest setSourcePorts(List sourcePorts) { + this.sourcePorts = sourcePorts; + return this; + } + public List getSourcePorts() { + return sourcePorts; + } + public TransportPolicyRequest setDestinationPorts(List destinationPorts) { + this.destinationPorts = destinationPorts; + return this; + } + public List getDestinationPorts() { + return destinationPorts; + } + public TransportPolicyRequest setPeers(List peers) { + this.peers = peers; + return this; + } + public List getPeers() { + return peers; + } + + @Override + public boolean equals(Object another) { + if (this != another) { + if (another == null || another.getClass() != TransportPolicyRequest.class) { + return false; + } + TransportPolicyRequest a = (TransportPolicyRequest) another; + if (direction == null ? a.direction != null : !direction.equals(a.direction)) { + return false; + } + if (identifier == null ? a.identifier != null : !identifier.equals(a.identifier)) { + return false; + } + if (subject == null ? a.subject != null : !subject.equals(a.subject)) { + return false; + } + if (conditions == null ? a.conditions != null : !conditions.equals(a.conditions)) { + return false; + } + if (sourcePorts == null ? a.sourcePorts != null : !sourcePorts.equals(a.sourcePorts)) { + return false; + } + if (destinationPorts == null ? a.destinationPorts != null : !destinationPorts.equals(a.destinationPorts)) { + return false; + } + if (peers == null ? a.peers != null : !peers.equals(a.peers)) { + return false; + } + } + return true; + } +} diff --git a/core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirement.java b/core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirement.java new file mode 100644 index 00000000000..9b0ff948ef7 --- /dev/null +++ b/core/msd/src/main/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirement.java @@ -0,0 +1,61 @@ +// +// This file generated by rdl 1.5.2. Do not modify! +// + +package com.yahoo.athenz.msd; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.yahoo.rdl.*; + +// +// TransportPolicySubjectSelectorRequirement - A subject selector requirement +// is a selector that contains value, a key, and an operator that relates the +// key and value. +// +@JsonIgnoreProperties(ignoreUnknown = true) +public class TransportPolicySubjectSelectorRequirement { + public String key; + public String operator; + public String value; + + public TransportPolicySubjectSelectorRequirement setKey(String key) { + this.key = key; + return this; + } + public String getKey() { + return key; + } + public TransportPolicySubjectSelectorRequirement setOperator(String operator) { + this.operator = operator; + return this; + } + public String getOperator() { + return operator; + } + public TransportPolicySubjectSelectorRequirement setValue(String value) { + this.value = value; + return this; + } + public String getValue() { + return value; + } + + @Override + public boolean equals(Object another) { + if (this != another) { + if (another == null || another.getClass() != TransportPolicySubjectSelectorRequirement.class) { + return false; + } + TransportPolicySubjectSelectorRequirement a = (TransportPolicySubjectSelectorRequirement) another; + if (key == null ? a.key != null : !key.equals(a.key)) { + return false; + } + if (operator == null ? a.operator != null : !operator.equals(a.operator)) { + return false; + } + if (value == null ? a.value != null : !value.equals(a.value)) { + return false; + } + } + return true; + } +} diff --git a/core/msd/src/main/rdl/KubernetesNetworkPolicy.rdli b/core/msd/src/main/rdl/KubernetesNetworkPolicy.rdli index 96316b8a245..1b11461cef1 100644 --- a/core/msd/src/main/rdl/KubernetesNetworkPolicy.rdli +++ b/core/msd/src/main/rdl/KubernetesNetworkPolicy.rdli @@ -9,7 +9,7 @@ resource KubernetesNetworkPolicyResponse POST "/domain/{domainName}/service/{ser DomainName domainName; //Name of the domain EntityName serviceName; //Name of the service KubernetesNetworkPolicyRequest request; //Struct representing input options based on the cluster context - authorize ("read", "{domainName}:service.{serviceName}"); + authorize ("msd.GetNetworkPolicy", "{domainName}:service.{serviceName}"); String matchingTag (header="If-None-Match"); //Retrieved from the previous request, this timestamp specifies to the server to return any policies modified since this time String tag (header="ETag", out); //The current latest modification timestamp is returned in this header expected OK, NOT_MODIFIED; diff --git a/core/msd/src/main/rdl/TransportPolicyRule.rdli b/core/msd/src/main/rdl/TransportPolicyRule.rdli index 0673d90236f..a2849941d57 100644 --- a/core/msd/src/main/rdl/TransportPolicyRule.rdli +++ b/core/msd/src/main/rdl/TransportPolicyRule.rdli @@ -20,10 +20,10 @@ resource TransportPolicyRules GET "/transportpolicies" { } } -// API to validate microsegmentation policies against network policies +// API to validate micro-segmentation policies against network policies resource TransportPolicyValidationResponse POST "/transportpolicy/validate" (name=validateTransportPolicy) { - TransportPolicyValidationRequest transportPolicy ; // Struct representing microsegmentation policy entered by the user + TransportPolicyValidationRequest transportPolicy ; // Struct representing micro-segmentation policy entered by the user expected OK; authenticate; exceptions { @@ -63,4 +63,20 @@ resource TransportPolicyRules GET "/domain/{domainName}/transportpolicies" (name ResourceError UNAUTHORIZED; ResourceError TOO_MANY_REQUESTS; } +} + +// API endpoint to create a transport policy for a given domain and service +resource TransportPolicyRules PUT "/domain/{domainName}/service/{serviceName}/transportpolicy" (name=putTransportPolicy) { + DomainName domainName; //name of the domain + EntityName serviceName; //Name of the service + TransportPolicyRequest payload; //Struct representing input transport policy + authorize ("msd.UpdateNetworkPolicy", "{domainName}:service.{serviceName}"); + expected NO_CONTENT, OK; + exceptions { + ResourceError BAD_REQUEST; + ResourceError NOT_FOUND; + ResourceError FORBIDDEN; + ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; + } } \ No newline at end of file diff --git a/core/msd/src/main/rdl/TransportPolicyRule.tdl b/core/msd/src/main/rdl/TransportPolicyRule.tdl index d6e315d021b..2cb0b19e833 100644 --- a/core/msd/src/main/rdl/TransportPolicyRule.tdl +++ b/core/msd/src/main/rdl/TransportPolicyRule.tdl @@ -120,4 +120,22 @@ type TransportPolicyValidationResponse Struct { // List of TransportPolicyValidationResponse type TransportPolicyValidationResponseList Struct { Array responseList; //list of transport policy validation response +} + +// A subject selector requirement is a selector that contains value, a key, and an operator that relates the key and value. +type TransportPolicySubjectSelectorRequirement Struct { + String key; //key that the selector applies to + String operator; //Operator that is applied to the key and value + String value; //Value that the selector applies to +} + +// Input to create a transport policy +type TransportPolicyRequest Struct { + TransportPolicyTrafficDirection direction; //Direction of network traffic + EntityName identifier; //Policy Identifier + TransportPolicySubject subject; //Subject for the policy + Array conditions (optional); //List of subject selector conditions + Array sourcePorts; //List of source network traffic ports + Array destinationPorts; //List of destination network traffic ports + Array peers (optional); //Source or destination of the policy depending on direction } \ No newline at end of file diff --git a/core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicyRequestTest.java b/core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicyRequestTest.java new file mode 100644 index 00000000000..ea519251df4 --- /dev/null +++ b/core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicyRequestTest.java @@ -0,0 +1,84 @@ +/* + * Copyright The Athenz Authors + * + * 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 com.yahoo.athenz.msd; +import org.testng.annotations.Test; + +import java.util.List; + +import static org.testng.Assert.*; + +public class TransportPolicyRequestTest { + + @Test + public void testMethods() { + TransportPolicyRequest t1 = new TransportPolicyRequest(); + t1.setDirection(TransportPolicyTrafficDirection.INGRESS); + t1.setIdentifier("id"); + t1.setSubject(new TransportPolicySubject().setDomainName("domain").setServiceName("service")); + t1.setConditions(List.of(new TransportPolicySubjectSelectorRequirement().setKey("key").setOperator("EQUALS").setValue("value"))); + t1.setSourcePorts(List.of(new TransportPolicyPort().setPort(1024).setEndPort(65535).setProtocol(TransportPolicyProtocol.TCP))); + t1.setDestinationPorts(List.of(new TransportPolicyPort().setPort(4443).setEndPort(4443).setProtocol(TransportPolicyProtocol.TCP))); + t1.setPeers(List.of(new TransportPolicySubject().setDomainName("domain2").setServiceName("service2"))); + + TransportPolicyRequest t2 = new TransportPolicyRequest(); + t2.setDirection(TransportPolicyTrafficDirection.INGRESS); + t2.setIdentifier("id"); + t2.setSubject(new TransportPolicySubject().setDomainName("domain").setServiceName("service")); + t2.setConditions(List.of(new TransportPolicySubjectSelectorRequirement().setKey("key").setOperator("EQUALS").setValue("value"))); + t2.setSourcePorts(List.of(new TransportPolicyPort().setPort(1024).setEndPort(65535).setProtocol(TransportPolicyProtocol.TCP))); + t2.setDestinationPorts(List.of(new TransportPolicyPort().setPort(4443).setEndPort(4443).setProtocol(TransportPolicyProtocol.TCP))); + t2.setPeers(List.of(new TransportPolicySubject().setDomainName("domain2").setServiceName("service2"))); + + assertEquals(t1, t2); + assertFalse(t1.equals("abc")); + + assertEquals(t1.getDirection(), TransportPolicyTrafficDirection.INGRESS); + assertEquals(t1.getIdentifier(), "id"); + assertEquals(t1.getSubject().getDomainName(), "domain"); + assertEquals(t1.getConditions().get(0).getKey(), "key"); + assertEquals(t1.getSourcePorts().get(0).getPort(), 1024); + assertEquals(t1.getDestinationPorts().get(0).getEndPort(), 4443); + assertEquals(t1.getPeers().get(0).getServiceName(), "service2"); + + t2.setDirection(TransportPolicyTrafficDirection.EGRESS); + assertNotEquals(t1, t2); + + t2.setDirection(TransportPolicyTrafficDirection.INGRESS); + t2.setIdentifier("id2"); + assertNotEquals(t1, t2); + + t2.setIdentifier("id"); + t2.setSubject(new TransportPolicySubject().setDomainName("domain").setServiceName("service2")); + assertNotEquals(t1, t2); + + t2.setSubject(new TransportPolicySubject().setDomainName("domain").setServiceName("service")); + t2.setConditions(new java.util.ArrayList<>()); + assertNotEquals(t1, t2); + + t2.setConditions(List.of(new TransportPolicySubjectSelectorRequirement().setKey("key").setOperator("EQUALS").setValue("value"))); + t2.setSourcePorts(new java.util.ArrayList<>()); + assertNotEquals(t1, t2); + + t2.setSourcePorts(List.of(new TransportPolicyPort().setPort(1024).setEndPort(65535).setProtocol(TransportPolicyProtocol.TCP))); + t2.setDestinationPorts(new java.util.ArrayList<>()); + assertNotEquals(t1, t2); + + t2.setDestinationPorts(List.of(new TransportPolicyPort().setPort(4443).setEndPort(4443).setProtocol(TransportPolicyProtocol.TCP))); + t2.setPeers(new java.util.ArrayList<>()); + assertNotEquals(t1, t2); + } +} \ No newline at end of file diff --git a/core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirementTest.java b/core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirementTest.java new file mode 100644 index 00000000000..e3519cbe78b --- /dev/null +++ b/core/msd/src/test/java/com/yahoo/athenz/msd/TransportPolicySubjectSelectorRequirementTest.java @@ -0,0 +1,53 @@ +/* + * Copyright The Athenz Authors + * + * 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 com.yahoo.athenz.msd; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class TransportPolicySubjectSelectorRequirementTest { + + @Test + public void testMethods() { + TransportPolicySubjectSelectorRequirement tps1 = new TransportPolicySubjectSelectorRequirement(); + tps1.setKey("key"); + tps1.setOperator("operator"); + tps1.setValue("value"); + + TransportPolicySubjectSelectorRequirement tps2 = new TransportPolicySubjectSelectorRequirement(); + tps2.setKey("key"); + tps2.setOperator("operator"); + tps2.setValue("value"); + + assertEquals(tps1.getKey(), "key"); + assertEquals(tps1.getOperator(), "operator"); + assertEquals(tps1.getValue(), "value"); + + assertEquals(tps1, tps2); + assertFalse(tps1.equals("abc")); + + tps2.setKey("key2"); + assertNotEquals(tps1, tps2); + + tps2.setKey("key"); + tps2.setOperator("operator2"); + assertNotEquals(tps1, tps2); + + tps2.setOperator("operator"); + tps2.setValue("value2"); + assertNotEquals(tps1, tps2); + } +} \ No newline at end of file