diff --git a/src/openapi/service.yaml b/src/openapi/service.yaml index f189e9b25..879c9eabe 100644 --- a/src/openapi/service.yaml +++ b/src/openapi/service.yaml @@ -203,72 +203,45 @@ IngressPublic: AplServiceSpec: type: object - allOf: - - type: object + properties: + namespace: + $ref: 'definitions.yaml#/idName' + description: A Kubernetes namespace. + port: + type: integer + minimum: 1 + maximum: 65535 + default: 80 + x-formtype: SelectWidget + ksvc: + type: object properties: - namespace: - $ref: 'definitions.yaml#/idName' - description: A Kubernetes namespace. - port: + predeployed: + description: Set this flag it the service is managed by Knative service. + type: boolean + default: false + trafficControl: + title: Traffic Control + description: Split traffic between multiple versions (blue-green, canary). + properties: + enabled: + type: boolean + default: false + weightV1: type: integer - minimum: 1 - maximum: 65535 - default: 80 - x-formtype: SelectWidget - ksvc: - type: object - properties: - predeployed: - description: Set this flag it the service is managed by Knative service. - type: boolean - default: false - trafficControl: - title: Traffic Control - description: Split traffic between multiple versions (blue-green, canary). - properties: - enabled: - type: boolean - default: false - weightV1: - type: integer - default: 90 - title: Weight v1 - description: 'Percentage of traffic to version 1.' - weightV2: - type: integer - default: 10 - title: Weight v2 - description: 'Percentage of traffic to version 2.' - - oneOf: - - $ref: '#/AplIngressCluster' - - $ref: '#/AplIngressPublic' - -AplIngressCluster: - additionalProperties: false - title: No Exposure - type: object - properties: - type: - type: string - enum: - - cluster - default: cluster - required: - - type - -AplIngressPublic: - type: object - properties: - type: - type: string - enum: - - public - default: public + default: 90 + title: Weight v1 + description: 'Percentage of traffic to version 1.' + weightV2: + type: integer + default: 10 + title: Weight v2 + description: 'Percentage of traffic to version 2.' ingressClassName: - description: Assign service to a paricular Load Balancer by selecting ingress class name. - title: Ingress Class Name - $ref: 'definitions.yaml#/idName' - default: 'platform' + description: Assign service to a paricular Load Balancer by selecting ingress class name. + title: Ingress Class Name + $ref: 'definitions.yaml#/idName' + default: 'platform' tlsPass: description: Pass through the request as is to the backing service. Only available for pre-deployed regular (non-Knative) services. title: TLS passthrough @@ -341,7 +314,3 @@ AplIngressPublic: required: - name - value - required: - - type - description: Will only accept traffic coming from an external loadbalancer. - title: External diff --git a/src/otomi-stack.test.ts b/src/otomi-stack.test.ts index 0a0f460ed..10f10ba9c 100644 --- a/src/otomi-stack.test.ts +++ b/src/otomi-stack.test.ts @@ -65,7 +65,6 @@ describe('Data validation', () => { }, }, spec: { - type: 'public', domain: 'b.a.com', }, status: {}, @@ -79,7 +78,6 @@ describe('Data validation', () => { }, }, spec: { - type: 'public', domain: 'b.a.com', paths: ['/test/'], }, @@ -98,7 +96,7 @@ describe('Data validation', () => { const svc: AplServiceRequest = { kind: 'AplTeamService', metadata: { name: 'svc' }, - spec: { type: 'public', domain: 'b.a.com' }, + spec: { domain: 'b.a.com' }, } expect(() => otomiStack.checkPublicUrlInUse(teamId, svc)).toThrow(new PublicUrlExists()) }) @@ -107,7 +105,7 @@ describe('Data validation', () => { const svc: AplServiceRequest = { kind: 'AplTeamService', metadata: { name: 'svc' }, - spec: { type: 'public', domain: 'b.a.com', paths: ['/test/'] }, + spec: { domain: 'b.a.com', paths: ['/test/'] }, } expect(() => otomiStack.checkPublicUrlInUse(teamId, svc)).toThrow(new PublicUrlExists()) }) @@ -116,7 +114,7 @@ describe('Data validation', () => { const svc: AplServiceRequest = { kind: 'AplTeamService', metadata: { name: 'svc' }, - spec: { type: 'public', domain: 'b.a.com', paths: ['/bla'] }, + spec: { domain: 'b.a.com', paths: ['/bla'] }, } expect(() => otomiStack.checkPublicUrlInUse(teamId, svc)).not.toThrow() }) @@ -125,7 +123,7 @@ describe('Data validation', () => { const svc: AplServiceRequest = { kind: 'AplTeamService', metadata: { name: 'svc' }, - spec: { type: 'cluster' }, + spec: {}, } expect(() => otomiStack.checkPublicUrlInUse(teamId, svc)).not.toThrow() }) @@ -134,7 +132,7 @@ describe('Data validation', () => { const svc: AplServiceRequest = { kind: 'AplTeamService', metadata: { name: 'svc' }, - spec: { type: 'public', domain: 'c.a.com' }, + spec: { domain: 'c.a.com' }, } expect(() => otomiStack.checkPublicUrlInUse(teamId, svc)).not.toThrow() diff --git a/src/otomi-stack.ts b/src/otomi-stack.ts index c59101703..3cfa59bc0 100644 --- a/src/otomi-stack.ts +++ b/src/otomi-stack.ts @@ -1149,7 +1149,7 @@ export default class OtomiStack { await this.createAplService(teamId, { kind: 'AplTeamService', metadata: { name: projectName }, - spec: { type: 'cluster', ...data.spec.service }, + spec: { ...data.spec.service }, }) } await this.saveTeamConfigItem(project) @@ -1936,28 +1936,23 @@ export default class OtomiStack { checkPublicUrlInUse(teamId: string, service: AplServiceRequest): void { // skip when editing or when svc is of type "cluster" as it has no url const newSvc = service.spec - if (newSvc?.type === 'public') { - const services = this.repoService.getTeamConfigService(teamId).getServices() - - const servicesFiltered = filter(services, (svc) => { - if (svc.spec.type === 'public') { - const { domain, paths } = svc.spec - - // no paths for existing or new service? then just check base url - if (!newSvc.paths?.length && !paths?.length) return domain === newSvc.domain - // one has paths but other doesn't? no problem - if ((newSvc.paths?.length && !paths?.length) || (!newSvc.paths?.length && paths?.length)) return false - // both have paths, so check full - return paths?.some((p) => { - const existingUrl = `${domain}${p}` - const newUrls: string[] = newSvc.paths?.map((_p: string) => `${domain}${_p}`) || [] - return newUrls.includes(existingUrl) - }) - } - return false + const services = this.repoService.getTeamConfigService(teamId).getServices() + + const servicesFiltered = filter(services, (svc) => { + const { domain, paths } = svc.spec + + // no paths for existing or new service? then just check base url + if (!newSvc.paths?.length && !paths?.length) return domain === newSvc.domain + // one has paths but other doesn't? no problem + if ((newSvc.paths?.length && !paths?.length) || (!newSvc.paths?.length && paths?.length)) return false + // both have paths, so check full + return paths?.some((p) => { + const existingUrl = `${domain}${p}` + const newUrls: string[] = newSvc.paths?.map((_p: string) => `${domain}${_p}`) || [] + return newUrls.includes(existingUrl) }) - if (servicesFiltered.length > 0) throw new PublicUrlExists() - } + }) + if (servicesFiltered.length > 0) throw new PublicUrlExists() } emitPipelineStatus(sha: string): void { @@ -2434,44 +2429,37 @@ export default class OtomiStack { 'cname', ] const inService = omit(serviceSpec, publicIngressFields) - if (serviceSpec.type === 'public') { - const { cluster, dns } = this.getSettings(['cluster', 'dns']) - const url = getServiceUrl({ - domain: serviceSpec.domain, - name: service.metadata.name, - teamId: service.metadata.labels['apl.io/teamId'], - cluster, - dns, - }) - return removeBlankAttributes({ - ...inService, - ...serviceMeta, - ingress: { - ...pick(serviceSpec, publicIngressFields), - domain: url.domain, - subdomain: url.subdomain, - useDefaultHost: !serviceSpec.domain && serviceSpec.ownHost, - }, - }) - } else { - return removeBlankAttributes({ - ...serviceMeta, - ...inService, - ingress: { type: 'cluster' }, - }) - } + + const { cluster, dns } = this.getSettings(['cluster', 'dns']) + const url = getServiceUrl({ + domain: serviceSpec.domain, + name: service.metadata.name, + teamId: service.metadata.labels['apl.io/teamId'], + cluster, + dns, + }) + return removeBlankAttributes({ + ...serviceMeta, + ...inService, + ingress: { + ...pick(serviceSpec, publicIngressFields), + domain: url.domain, + subdomain: url.subdomain, + useDefaultHost: !serviceSpec.domain && serviceSpec.ownHost, + }, + }) } convertDbServiceToValues(svc: Service): ServiceSpec { const { name } = svc const svcCommon = omit(svc, ['name', 'ingress', 'path']) if (svc.ingress?.type === 'public') { - const ing = svc.ingress - const domain = ing.subdomain ? `${ing.subdomain}.${ing.domain}` : ing.domain + const { ingress } = svc + const domain = ingress.subdomain ? `${ingress.subdomain}.${ingress.domain}` : ingress.domain return { name, ...svcCommon, - ...pick(ing, [ + ...pick(ingress, [ 'hasCert', 'certName', 'paths', @@ -2482,15 +2470,13 @@ export default class OtomiStack { 'useCname', 'cname', ]), - type: 'public', - ownHost: ing.useDefaultHost, - domain: ing.useDefaultHost ? undefined : domain, + ownHost: ingress.useDefaultHost, + domain: ingress.useDefaultHost ? undefined : domain, } } else { return { name, ...svcCommon, - type: svc.ingress?.type || 'cluster', } } } diff --git a/src/services/TeamConfigService.test.ts b/src/services/TeamConfigService.test.ts index f752857f8..14c96e727 100644 --- a/src/services/TeamConfigService.test.ts +++ b/src/services/TeamConfigService.test.ts @@ -1,4 +1,5 @@ // Mock UUID to generate predictable values +import { AlreadyExists, NotExistError } from '../error' import { AplBackupRequest, AplBuildRequest, @@ -11,7 +12,6 @@ import { TeamConfig, } from '../otomi-models' import { TeamConfigService } from './TeamConfigService' -import { AlreadyExists, NotExistError } from '../error' jest.mock('uuid', () => ({ v4: jest.fn(() => 'mocked-uuid'), @@ -154,7 +154,7 @@ describe('TeamConfigService', () => { const serviceData: AplServiceRequest = { kind: 'AplTeamService', metadata: { name: 'TestService' }, - spec: { type: 'public' }, + spec: {}, } test('should create a service', () => { const createdService = service.createService(serviceData) @@ -167,7 +167,7 @@ describe('TeamConfigService', () => { 'apl.io/teamId': 'team1', }, }, - spec: { type: 'public' }, + spec: {}, status: {}, }) expect(service.getServices()).toHaveLength(1) @@ -459,7 +459,7 @@ describe('TeamConfigService', () => { service.createService({ kind: 'AplTeamService', metadata: { name: 'ExistingService' }, - spec: { type: 'public' }, + spec: {}, }) expect(service.doesProjectNameExist('ExistingService')).toBe(true) }) @@ -478,7 +478,7 @@ describe('TeamConfigService', () => { service.createService({ kind: 'AplTeamService', metadata: { name: 'SomeService' }, - spec: { type: 'public' }, + spec: {}, }) expect(service.doesProjectNameExist('NonExistentProject')).toBe(false) })