1+ import { ApiObject , Lazy , Duration } from 'cdk8s' ;
2+ import { Construct , IConstruct } from 'constructs' ;
3+ import * as base from './base' ;
4+ import * as k8s from './imports/k8s' ;
5+ import * as selector from './selector' ;
6+ import * as util from './util' ;
7+
8+ /**
9+ * Options for `Service`.
10+ */
11+ export interface ServiceProps {
12+ /**
13+ * The IP address of the service and is usually assigned randomly by the
14+ * master. If an address is specified manually and is not in use by others,
15+ * it will be allocated to the service; otherwise, creation of the service
16+ * will fail. This field can not be changed through updates. Valid values
17+ * are "None", empty string (""), or a valid IP address. "None" can be
18+ * specified for headless services when proxying is not required. Only
19+ * applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type
20+ * is ExternalName.
21+ *
22+ * @default - Automatically assigned.
23+ */
24+ readonly clusterIP ?: string ;
25+
26+ /**
27+ * A list of IP addresses for which nodes in the cluster will also accept
28+ * traffic for this service. These IPs are not managed by Kubernetes.
29+ *
30+ * @default - No external IPs.
31+ */
32+ readonly externalIPs ?: string [ ] ;
33+
34+ /**
35+ * Determines how the Service is exposed.
36+ *
37+ * @default ServiceType.CLUSTER_IP
38+ */
39+ readonly type ?: ServiceType ;
40+
41+ /**
42+ * The list of ports that are exposed by this service.
43+ *
44+ * @default - No ports.
45+ */
46+ readonly ports ?: ServicePort [ ] ;
47+
48+ /**
49+ * Label selector for pods. Only pods matching the selector will be
50+ * considered by this service.
51+ *
52+ * @default - No selector.
53+ */
54+ readonly selector ?: selector . PodSelector ;
55+
56+ /**
57+ * The external name of the service. Only applies to services of
58+ * type ExternalName.
59+ *
60+ * @default - No external name.
61+ */
62+ readonly externalName ?: string ;
63+ }
64+
65+ /**
66+ * Constant values for clusterIP.
67+ */
68+ export class ClusterIP {
69+ /**
70+ * Used to specify a headless service.
71+ */
72+ public static readonly NONE = 'None' ;
73+ }
74+
75+ /**
76+ * Service types.
77+ */
78+ export enum ServiceType {
79+ /**
80+ * Exposes the service on a cluster-internal IP.
81+ */
82+ CLUSTER_IP = 'ClusterIP' ,
83+
84+ /**
85+ * Exposes the service on each Node's IP at a static port.
86+ */
87+ NODE_PORT = 'NodePort' ,
88+
89+ /**
90+ * Exposes the service externally using a cloud load balancer.
91+ */
92+ LOAD_BALANCER = 'LoadBalancer' ,
93+
94+ /**
95+ * Maps the service to the contents of the externalName field.
96+ */
97+ EXTERNAL_NAME = 'ExternalName' ,
98+ }
99+
100+ /**
101+ * Options for a service port.
102+ */
103+ export interface ServicePort {
104+ /**
105+ * The port number.
106+ */
107+ readonly port : number ;
108+
109+ /**
110+ * The port on the pod to target.
111+ *
112+ * @default - The value of `port` is used.
113+ */
114+ readonly targetPort ?: number ;
115+
116+ /**
117+ * The name of the port.
118+ *
119+ * @default - No name.
120+ */
121+ readonly name ?: string ;
122+
123+ /**
124+ * The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
125+ *
126+ * @default Protocol.TCP
127+ */
128+ readonly protocol ?: Protocol ;
129+ }
130+
131+ /**
132+ * Network protocols.
133+ */
134+ export enum Protocol {
135+ TCP = 'TCP' ,
136+ UDP = 'UDP' ,
137+ SCTP = 'SCTP' ,
138+ }
139+
140+ /**
141+ * A Kubernetes service.
142+ */
143+ export class Service extends base . Resource {
144+
145+ /**
146+ * The type of the service.
147+ */
148+ public readonly type : ServiceType ;
149+
150+ /**
151+ * The cluster IP address of the service.
152+ */
153+ public readonly clusterIP ?: string ;
154+
155+ /**
156+ * The external name of the service.
157+ */
158+ public readonly externalName ?: string ;
159+
160+ /**
161+ * The external IP addresses of the service.
162+ */
163+ public readonly externalIPs : string [ ] ;
164+
165+ private readonly _ports : ServicePort [ ] ;
166+ private readonly _selector : { [ key : string ] : string } ;
167+
168+ /**
169+ * Whether the service is headless.
170+ */
171+ private get _isHeadless ( ) : boolean {
172+ return this . clusterIP === ClusterIP . NONE ;
173+ }
174+
175+ constructor ( scope : Construct , id : string , props : ServiceProps = { } ) {
176+ super ( scope , id ) ;
177+
178+ this . type = props . type ?? ServiceType . CLUSTER_IP ;
179+ this . clusterIP = props . clusterIP ;
180+ this . externalName = props . externalName ;
181+ this . externalIPs = props . externalIPs ?? [ ] ;
182+ this . _ports = props . ports ?? [ ] ;
183+ this . _selector = props . selector ?. _selector ( ) ?? { } ;
184+ }
185+
186+ /**
187+ * Add a port to the service.
188+ */
189+ public addPort ( port : ServicePort ) : void {
190+ this . _ports . push ( port ) ;
191+ }
192+
193+ /**
194+ * The ports exposed by the service.
195+ */
196+ public get ports ( ) : ServicePort [ ] {
197+ return [ ...this . _ports ] ;
198+ }
199+
200+ /**
201+ * @internal
202+ */
203+ public _toKube ( ) : k8s . KubeServiceProps {
204+ const ports : k8s . ServicePort [ ] = this . _ports . map ( p => ( {
205+ port : p . port ,
206+ targetPort : p . targetPort ?? p . port ,
207+ name : p . name ,
208+ protocol : p . protocol ,
209+ } ) ) ;
210+
211+ return {
212+ spec : {
213+ type : this . type ,
214+ clusterIP : this . clusterIP ,
215+ externalName : this . externalName ,
216+ externalIPs : this . externalIPs ,
217+ selector : this . _selector ,
218+ ports : ports . length > 0 ? ports : undefined ,
219+ } ,
220+ } ;
221+ }
222+
223+ /**
224+ * @internal
225+ */
226+ public _toKubeApiObject ( ) : k8s . KubeService {
227+ return new k8s . KubeService ( this , 'Service' , this . _toKube ( ) ) ;
228+ }
229+
230+ /**
231+ * Validate the service.
232+ */
233+ protected validate ( ) : string [ ] {
234+ const errors : string [ ] = [ ] ;
235+
236+ if ( this . _ports . length === 0 && ! this . _isHeadless ) {
237+ errors . push ( 'A service must be configured with a port' ) ;
238+ }
239+
240+ if ( this . _ports . length === 0 && this . type !== ServiceType . CLUSTER_IP && ! this . _isHeadless ) {
241+ errors . push ( 'A non-headless service of type ' + this . type + ' must have at least one port' ) ;
242+ }
243+
244+ return errors ;
245+ }
246+ }
0 commit comments