From b3f86805590c2cb9bbdf731f957a2b2b652ecba0 Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Tue, 6 Aug 2024 14:07:42 -0700 Subject: [PATCH] merge #2660 from master to jetty 1.12.x branch (#2675) Signed-off-by: Henry Avetisyan --- clients/go/msd/client.go | 58 +++++++++ clients/go/msd/model.go | 105 ++++++++++++++++ clients/go/msd/msd_schema.go | 41 ++++++ .../athenz/msd/MSDRDLGeneratedClient.java | 61 +++++++++ .../yahoo/athenz/msd/CompositeInstance.java | 112 +++++++++++++++++ .../java/com/yahoo/athenz/msd/MSDSchema.java | 48 +++++++ core/msd/src/main/rdl/Workload.rdli | 34 ++++- core/msd/src/main/rdl/Workload.tdl | 11 ++ .../athenz/msd/CompositeInstanceTest.java | 118 ++++++++++++++++++ 9 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 core/msd/src/main/java/com/yahoo/athenz/msd/CompositeInstance.java create mode 100644 core/msd/src/test/java/com/yahoo/athenz/msd/CompositeInstanceTest.java diff --git a/clients/go/msd/client.go b/clients/go/msd/client.go index 055bebe6f66..3b9355bfdfc 100644 --- a/clients/go/msd/client.go +++ b/clients/go/msd/client.go @@ -851,6 +851,64 @@ func (client MSDClient) GetWorkloadsByDomainAndService(request *BulkWorkloadRequ } } +func (client MSDClient) PutCompositeInstance(domainName DomainName, serviceName EntityName, instance *CompositeInstance) error { + url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/workload/discover/instance" + contentBytes, err := json.Marshal(instance) + if err != nil { + return err + } + resp, err := client.httpPut(url, nil, contentBytes) + if err != nil { + return err + } + defer resp.Body.Close() + switch resp.StatusCode { + case 204: + return nil + default: + var errobj rdl.ResourceError + contentBytes, err = io.ReadAll(resp.Body) + if err != nil { + return err + } + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return errobj + } +} + +func (client MSDClient) DeleteCompositeInstance(domainName DomainName, serviceName EntityName, instance SimpleName) error { + url := client.URL + "/domain/" + fmt.Sprint(domainName) + "/service/" + fmt.Sprint(serviceName) + "/workload/discover/instance/" + fmt.Sprint(instance) + resp, err := client.httpDelete(url, nil) + if err != nil { + return err + } + defer resp.Body.Close() + switch resp.StatusCode { + case 204: + return nil + default: + var errobj rdl.ResourceError + contentBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return errobj + } +} + func (client MSDClient) EvaluateNetworkPolicyChange(detail *NetworkPolicyChangeImpactRequest) (*NetworkPolicyChangeImpactResponse, error) { var data *NetworkPolicyChangeImpactResponse url := client.URL + "/transportpolicy/evaluatenetworkpolicychange" diff --git a/clients/go/msd/model.go b/clients/go/msd/model.go index daa4dc9299e..7159d906fe3 100644 --- a/clients/go/msd/model.go +++ b/clients/go/msd/model.go @@ -2271,6 +2271,111 @@ func (self *BulkWorkloadResponse) Validate() error { return nil } +// CompositeInstance - generic instance +type CompositeInstance struct { + + // + // name of the domain + // + DomainName DomainName `json:"domainName"` + + // + // name of the service + // + ServiceName EntityName `json:"serviceName"` + + // + // instance name/id + // + Instance SimpleName `json:"instance"` + + // + // instance type + // + InstanceType string `json:"instanceType" rdl:"optional" yaml:",omitempty"` + + // + // name of the instance provider, for example aws/gcp + // + Provider string `json:"provider" rdl:"optional" yaml:",omitempty"` + + // + // certificate expiry time (ex: getNotAfter), if applicable + // + CertExpiryTime *rdl.Timestamp `json:"certExpiryTime,omitempty" rdl:"optional" yaml:",omitempty"` + + // + // certificate issue time (ex: getNotBefore), if applicable + // + CertIssueTime *rdl.Timestamp `json:"certIssueTime,omitempty" rdl:"optional" yaml:",omitempty"` +} + +// NewCompositeInstance - creates an initialized CompositeInstance instance, returns a pointer to it +func NewCompositeInstance(init ...*CompositeInstance) *CompositeInstance { + var o *CompositeInstance + if len(init) == 1 { + o = init[0] + } else { + o = new(CompositeInstance) + } + return o +} + +type rawCompositeInstance CompositeInstance + +// UnmarshalJSON is defined for proper JSON decoding of a CompositeInstance +func (self *CompositeInstance) UnmarshalJSON(b []byte) error { + var m rawCompositeInstance + err := json.Unmarshal(b, &m) + if err == nil { + o := CompositeInstance(m) + *self = o + err = self.Validate() + } + return err +} + +// Validate - checks for missing required fields, etc +func (self *CompositeInstance) Validate() error { + if self.DomainName == "" { + return fmt.Errorf("CompositeInstance.domainName is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "DomainName", self.DomainName) + if !val.Valid { + return fmt.Errorf("CompositeInstance.domainName does not contain a valid DomainName (%v)", val.Error) + } + } + if self.ServiceName == "" { + return fmt.Errorf("CompositeInstance.serviceName is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "EntityName", self.ServiceName) + if !val.Valid { + return fmt.Errorf("CompositeInstance.serviceName does not contain a valid EntityName (%v)", val.Error) + } + } + if self.Instance == "" { + return fmt.Errorf("CompositeInstance.instance is missing but is a required field") + } else { + val := rdl.Validate(MSDSchema(), "SimpleName", self.Instance) + if !val.Valid { + return fmt.Errorf("CompositeInstance.instance does not contain a valid SimpleName (%v)", val.Error) + } + } + if self.InstanceType != "" { + val := rdl.Validate(MSDSchema(), "String", self.InstanceType) + if !val.Valid { + return fmt.Errorf("CompositeInstance.instanceType does not contain a valid String (%v)", val.Error) + } + } + if self.Provider != "" { + val := rdl.Validate(MSDSchema(), "String", self.Provider) + if !val.Valid { + return fmt.Errorf("CompositeInstance.provider does not contain a valid String (%v)", val.Error) + } + } + return nil +} + // NetworkPolicyChangeEffect - IMPACT indicates that a change in network policy // will interfere with workings of one or more transport policies NO_IMPACT // indicates that a change in network policy will not interfere with workings of diff --git a/clients/go/msd/msd_schema.go b/clients/go/msd/msd_schema.go index 2515e9d3bd4..705e3b147b5 100644 --- a/clients/go/msd/msd_schema.go +++ b/clients/go/msd/msd_schema.go @@ -325,6 +325,17 @@ func init() { tBulkWorkloadResponse.Field("workloads", "Workloads", false, nil, "matching workloads") sb.AddType(tBulkWorkloadResponse.Build()) + tCompositeInstance := rdl.NewStructTypeBuilder("Struct", "CompositeInstance") + tCompositeInstance.Comment("generic instance") + tCompositeInstance.Field("domainName", "DomainName", false, nil, "name of the domain") + tCompositeInstance.Field("serviceName", "EntityName", false, nil, "name of the service") + tCompositeInstance.Field("instance", "SimpleName", false, nil, "instance name/id") + tCompositeInstance.Field("instanceType", "String", true, nil, "instance type") + tCompositeInstance.Field("provider", "String", true, nil, "name of the instance provider, for example aws/gcp") + tCompositeInstance.Field("certExpiryTime", "Timestamp", true, nil, "certificate expiry time (ex: getNotAfter), if applicable") + tCompositeInstance.Field("certIssueTime", "Timestamp", true, nil, "certificate issue time (ex: getNotBefore), if applicable") + sb.AddType(tCompositeInstance.Build()) + tNetworkPolicyChangeEffect := rdl.NewEnumTypeBuilder("Enum", "NetworkPolicyChangeEffect") tNetworkPolicyChangeEffect.Comment("IMPACT indicates that a change in network policy will interfere with workings of one or more transport policies NO_IMPACT indicates that a change in network policy will not interfere with workings of any transport policy") tNetworkPolicyChangeEffect.Element("IMPACT", "") @@ -885,6 +896,36 @@ func init() { mGetWorkloadsByDomainAndService.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(mGetWorkloadsByDomainAndService.Build()) + mPutCompositeInstance := rdl.NewResourceBuilder("Workloads", "PUT", "/domain/{domainName}/service/{serviceName}/workload/discover/instance") + mPutCompositeInstance.Comment("Api to discover an additional instance which can have static or dynamic or both IPs") + mPutCompositeInstance.Name("putCompositeInstance") + mPutCompositeInstance.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") + mPutCompositeInstance.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") + mPutCompositeInstance.Input("instance", "CompositeInstance", false, "", "", false, nil, "Generic instance") + mPutCompositeInstance.Auth("update", "{domainName}:service.{serviceName}", false, "") + mPutCompositeInstance.Expected("NO_CONTENT") + mPutCompositeInstance.Exception("BAD_REQUEST", "ResourceError", "") + mPutCompositeInstance.Exception("FORBIDDEN", "ResourceError", "") + mPutCompositeInstance.Exception("NOT_FOUND", "ResourceError", "") + mPutCompositeInstance.Exception("TOO_MANY_REQUESTS", "ResourceError", "") + mPutCompositeInstance.Exception("UNAUTHORIZED", "ResourceError", "") + sb.AddResource(mPutCompositeInstance.Build()) + + mDeleteCompositeInstance := rdl.NewResourceBuilder("Workloads", "DELETE", "/domain/{domainName}/service/{serviceName}/workload/discover/instance/{instance}") + mDeleteCompositeInstance.Comment("Api to delete an additional instance which can have static or dynamic or both IPs") + mDeleteCompositeInstance.Name("deleteCompositeInstance") + mDeleteCompositeInstance.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain") + mDeleteCompositeInstance.Input("serviceName", "EntityName", true, "", "", false, nil, "name of the service") + mDeleteCompositeInstance.Input("instance", "SimpleName", true, "", "", false, nil, "instance name/id/key") + mDeleteCompositeInstance.Auth("update", "{domainName}:service.{serviceName}", false, "") + mDeleteCompositeInstance.Expected("NO_CONTENT") + mDeleteCompositeInstance.Exception("BAD_REQUEST", "ResourceError", "") + mDeleteCompositeInstance.Exception("FORBIDDEN", "ResourceError", "") + mDeleteCompositeInstance.Exception("NOT_FOUND", "ResourceError", "") + mDeleteCompositeInstance.Exception("TOO_MANY_REQUESTS", "ResourceError", "") + mDeleteCompositeInstance.Exception("UNAUTHORIZED", "ResourceError", "") + sb.AddResource(mDeleteCompositeInstance.Build()) + mEvaluateNetworkPolicyChange := rdl.NewResourceBuilder("NetworkPolicyChangeImpactResponse", "POST", "/transportpolicy/evaluatenetworkpolicychange") mEvaluateNetworkPolicyChange.Comment("API to evaluate network policies change impact on transport policies") mEvaluateNetworkPolicyChange.Name("evaluateNetworkPolicyChange") 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 4b41f9537b4..ead0df8f9af 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 @@ -672,6 +672,67 @@ public BulkWorkloadResponse getWorkloadsByDomainAndService(BulkWorkloadRequest r } } + public Workloads putCompositeInstance(String domainName, String serviceName, CompositeInstance instance) throws URISyntaxException, IOException { + UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/service/{serviceName}/workload/discover/instance") + .resolveTemplate("domainName", domainName) + .resolveTemplate("serviceName", serviceName); + URIBuilder uriBuilder = new URIBuilder(uriTemplateBuilder.getUri()); + HttpEntity httpEntity = new StringEntity(jsonMapper.writeValueAsString(instance), 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: + return null; + 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 deleteCompositeInstance(String domainName, String serviceName, String instance) throws URISyntaxException, IOException { + UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/domain/{domainName}/service/{serviceName}/workload/discover/instance/{instance}") + .resolveTemplate("domainName", domainName) + .resolveTemplate("serviceName", serviceName) + .resolveTemplate("instance", instance); + URIBuilder uriBuilder = new URIBuilder(uriTemplateBuilder.getUri()); + HttpUriRequest httpUriRequest = RequestBuilder.delete() + .setUri(uriBuilder.build()) + .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: + return null; + 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 NetworkPolicyChangeImpactResponse evaluateNetworkPolicyChange(NetworkPolicyChangeImpactRequest detail) throws URISyntaxException, IOException { UriTemplateBuilder uriTemplateBuilder = new UriTemplateBuilder(baseUrl, "/transportpolicy/evaluatenetworkpolicychange"); URIBuilder uriBuilder = new URIBuilder(uriTemplateBuilder.getUri()); diff --git a/core/msd/src/main/java/com/yahoo/athenz/msd/CompositeInstance.java b/core/msd/src/main/java/com/yahoo/athenz/msd/CompositeInstance.java new file mode 100644 index 00000000000..58d652cc720 --- /dev/null +++ b/core/msd/src/main/java/com/yahoo/athenz/msd/CompositeInstance.java @@ -0,0 +1,112 @@ +// +// 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 com.yahoo.rdl.*; + +// +// CompositeInstance - generic instance +// +@JsonIgnoreProperties(ignoreUnknown = true) +public class CompositeInstance { + public String domainName; + public String serviceName; + public String instance; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String instanceType; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String provider; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public Timestamp certExpiryTime; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public Timestamp certIssueTime; + + public CompositeInstance setDomainName(String domainName) { + this.domainName = domainName; + return this; + } + public String getDomainName() { + return domainName; + } + public CompositeInstance setServiceName(String serviceName) { + this.serviceName = serviceName; + return this; + } + public String getServiceName() { + return serviceName; + } + public CompositeInstance setInstance(String instance) { + this.instance = instance; + return this; + } + public String getInstance() { + return instance; + } + public CompositeInstance setInstanceType(String instanceType) { + this.instanceType = instanceType; + return this; + } + public String getInstanceType() { + return instanceType; + } + public CompositeInstance setProvider(String provider) { + this.provider = provider; + return this; + } + public String getProvider() { + return provider; + } + public CompositeInstance setCertExpiryTime(Timestamp certExpiryTime) { + this.certExpiryTime = certExpiryTime; + return this; + } + public Timestamp getCertExpiryTime() { + return certExpiryTime; + } + public CompositeInstance setCertIssueTime(Timestamp certIssueTime) { + this.certIssueTime = certIssueTime; + return this; + } + public Timestamp getCertIssueTime() { + return certIssueTime; + } + + @Override + public boolean equals(Object another) { + if (this != another) { + if (another == null || another.getClass() != CompositeInstance.class) { + return false; + } + CompositeInstance a = (CompositeInstance) another; + if (domainName == null ? a.domainName != null : !domainName.equals(a.domainName)) { + return false; + } + if (serviceName == null ? a.serviceName != null : !serviceName.equals(a.serviceName)) { + return false; + } + if (instance == null ? a.instance != null : !instance.equals(a.instance)) { + return false; + } + if (instanceType == null ? a.instanceType != null : !instanceType.equals(a.instanceType)) { + return false; + } + if (provider == null ? a.provider != null : !provider.equals(a.provider)) { + return false; + } + if (certExpiryTime == null ? a.certExpiryTime != null : !certExpiryTime.equals(a.certExpiryTime)) { + return false; + } + if (certIssueTime == null ? a.certIssueTime != null : !certIssueTime.equals(a.certIssueTime)) { + return false; + } + } + return true; + } +} 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 370e2845b46..8c7ddebfc7a 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 @@ -278,6 +278,16 @@ private static Schema build() { .arrayField("unmodifiedServices", "DomainServices", false, "list of services grouped by domain, those are not changed since time stamp in matchingTag") .field("workloads", "Workloads", false, "matching workloads"); + sb.structType("CompositeInstance") + .comment("generic instance") + .field("domainName", "DomainName", false, "name of the domain") + .field("serviceName", "EntityName", false, "name of the service") + .field("instance", "SimpleName", false, "instance name/id") + .field("instanceType", "String", true, "instance type") + .field("provider", "String", true, "name of the instance provider, for example aws/gcp") + .field("certExpiryTime", "Timestamp", true, "certificate expiry time (ex: getNotAfter), if applicable") + .field("certIssueTime", "Timestamp", true, "certificate issue time (ex: getNotBefore), if applicable"); + sb.enumType("NetworkPolicyChangeEffect") .comment("IMPACT indicates that a change in network policy will interfere with workings of one or more transport policies NO_IMPACT indicates that a change in network policy will not interfere with workings of any transport policy") .element("IMPACT") @@ -869,6 +879,44 @@ private static Schema build() { .exception("UNAUTHORIZED", "ResourceError", "") ; + sb.resource("CompositeInstance", "PUT", "/domain/{domainName}/service/{serviceName}/workload/discover/instance") + .comment("Api to discover an additional instance which can have static or dynamic or both IPs") + .name("putCompositeInstance") + .pathParam("domainName", "DomainName", "name of the domain") + .pathParam("serviceName", "EntityName", "name of the service") + .input("instance", "CompositeInstance", "Generic instance") + .auth("update", "{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", "DELETE", "/domain/{domainName}/service/{serviceName}/workload/discover/instance/{instance}") + .comment("Api to delete an additional instance which can have static or dynamic or both IPs") + .name("deleteCompositeInstance") + .pathParam("domainName", "DomainName", "name of the domain") + .pathParam("serviceName", "EntityName", "name of the service") + .pathParam("instance", "SimpleName", "instance name/id/key") + .auth("update", "{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("NetworkPolicyChangeImpactRequest", "POST", "/transportpolicy/evaluatenetworkpolicychange") .comment("API to evaluate network policies change impact on transport policies") .name("evaluateNetworkPolicyChange") diff --git a/core/msd/src/main/rdl/Workload.rdli b/core/msd/src/main/rdl/Workload.rdli index 0baf690cf2e..aae72dcc6cd 100644 --- a/core/msd/src/main/rdl/Workload.rdli +++ b/core/msd/src/main/rdl/Workload.rdli @@ -145,4 +145,36 @@ resource BulkWorkloadResponse POST "/workloads" (name=getWorkloadsByDomainAndSer ResourceError UNAUTHORIZED; ResourceError TOO_MANY_REQUESTS; } -} \ No newline at end of file +} + +// Api to discover an additional instance which can have static or dynamic or both IPs +resource Workloads PUT "/domain/{domainName}/service/{serviceName}/workload/discover/instance" (name=putCompositeInstance) { + DomainName domainName; // name of the domain + EntityName serviceName; // name of the service + CompositeInstance instance; // Generic instance + authorize ("update", "{domainName}:service.{serviceName}"); + expected NO_CONTENT; + exceptions { + ResourceError BAD_REQUEST; + ResourceError NOT_FOUND; + ResourceError FORBIDDEN; + ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; + } +} + +// Api to delete an additional instance which can have static or dynamic or both IPs +resource Workloads DELETE "/domain/{domainName}/service/{serviceName}/workload/discover/instance/{instance}" (name=deleteCompositeInstance) { + DomainName domainName; // name of the domain + EntityName serviceName; // name of the service + SimpleName instance; // instance name/id/key + authorize ("update", "{domainName}:service.{serviceName}"); + expected NO_CONTENT; + exceptions { + ResourceError BAD_REQUEST; + ResourceError NOT_FOUND; + ResourceError FORBIDDEN; + ResourceError UNAUTHORIZED; + ResourceError TOO_MANY_REQUESTS; + } +} diff --git a/core/msd/src/main/rdl/Workload.tdl b/core/msd/src/main/rdl/Workload.tdl index d89dc976939..d675eceef85 100644 --- a/core/msd/src/main/rdl/Workload.tdl +++ b/core/msd/src/main/rdl/Workload.tdl @@ -87,4 +87,15 @@ type BulkWorkloadRequest Struct { type BulkWorkloadResponse Struct { Array unmodifiedServices; // list of services grouped by domain, those are not changed since time stamp in matchingTag Workloads workloads; // matching workloads +} + +// generic instance +type CompositeInstance Struct { + DomainName domainName; // name of the domain + EntityName serviceName; // name of the service + SimpleName instance; // instance name/id + String instanceType (optional); // instance type + String provider (optional); // name of the instance provider, for example aws/gcp + Timestamp certExpiryTime (optional); // certificate expiry time (ex: getNotAfter), if applicable + Timestamp certIssueTime (optional); // certificate issue time (ex: getNotBefore), if applicable } \ No newline at end of file diff --git a/core/msd/src/test/java/com/yahoo/athenz/msd/CompositeInstanceTest.java b/core/msd/src/test/java/com/yahoo/athenz/msd/CompositeInstanceTest.java new file mode 100644 index 00000000000..7dc9e7fa479 --- /dev/null +++ b/core/msd/src/test/java/com/yahoo/athenz/msd/CompositeInstanceTest.java @@ -0,0 +1,118 @@ +/* + * 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 static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotNull; + +import com.yahoo.rdl.Schema; +import com.yahoo.rdl.Timestamp; +import com.yahoo.rdl.Validator; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.List; + +public class CompositeInstanceTest { + @Test + public void testCompositeInstanceFields() { + CompositeInstance instance = new CompositeInstance(); + List ipAddresses = Collections.singletonList("10.20.30.40"); + instance.setDomainName("athenz") + .setServiceName("api") + .setInstance("i-1333w3er3rr") + .setInstanceType("ec2") + .setProvider("aws") + .setCertExpiryTime(Timestamp.fromMillis(1722347760000L)) + .setCertIssueTime(Timestamp.fromMillis(1722260648000L)); + + assertNotNull(instance); + assertEquals(instance.getDomainName(), "athenz"); + assertEquals(instance.getServiceName(), "api"); + assertEquals(instance.getInstance(), "i-1333w3er3rr"); + assertEquals(instance.getInstanceType(), "ec2"); + assertEquals(instance.getProvider(), "aws"); + assertEquals(instance.getCertExpiryTime(), Timestamp.fromMillis(1722347760000L)); + assertEquals(instance.getCertIssueTime(), Timestamp.fromMillis(1722260648000L)); + assertEquals(instance, instance); + + CompositeInstance instance2 = new CompositeInstance(); + instance2.setDomainName("athenz") + .setServiceName("api") + .setInstance("i-1333w3er3rr") + .setInstanceType("ec2") + .setProvider("aws") + .setCertExpiryTime(Timestamp.fromMillis(1722347760000L)) + .setCertIssueTime(Timestamp.fromMillis(1722260648000L)); + + assertEquals(instance, instance2); + + instance2.setDomainName("sports"); + assertNotEquals(instance, instance2); + instance2.setDomainName(null); + assertNotEquals(instance, instance2); + + instance2.setDomainName("athenz"); + instance2.setServiceName("apiv2"); + assertNotEquals(instance, instance2); + instance2.setServiceName(null); + assertNotEquals(instance, instance2); + + instance2.setServiceName("api"); + instance2.setInstance("i-2423w3er3rr"); + assertNotEquals(instance, instance2); + instance2.setInstance(null); + assertNotEquals(instance, instance2); + + instance2.setInstance("i-1333w3er3rr"); + instance2.setInstanceType("vm"); + assertNotEquals(instance, instance2); + instance2.setInstanceType(null); + assertNotEquals(instance, instance2); + + instance2.setInstanceType("ec2"); + instance2.setProvider("gcp"); + assertNotEquals(instance, instance2); + instance2.setProvider(null); + assertNotEquals(instance, instance2); + + instance2.setProvider("aws"); + instance2.setCertExpiryTime(Timestamp.fromMillis(1722247760000L)); + assertNotEquals(instance, instance2); + instance2.setCertExpiryTime(null); + assertNotEquals(instance, instance2); + + instance2.setCertExpiryTime(Timestamp.fromMillis(1722347760000L)); + instance2.setCertIssueTime(Timestamp.fromMillis(1722230648000L)); + assertNotEquals(instance, instance2); + instance2.setCertIssueTime(null); + assertNotEquals(instance, instance2); + + instance2.setCertIssueTime(Timestamp.fromMillis(1722260648000L)); + assertEquals(instance, instance2); + + assertNotEquals(instance, null); + // for code coverage + assertFalse(instance.equals("mystring")); + assertNotEquals(instance, "mystring"); + + assertEquals(instance, instance); + } +} \ No newline at end of file