Skip to content

Commit 94d0b51

Browse files
add custom domain support for containers
1 parent e9d8da1 commit 94d0b51

File tree

6 files changed

+180
-21
lines changed

6 files changed

+180
-21
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ serverless invoke --function first
3232

3333
Serverless Framework handles everything from creating namespaces to function/code deployment by calling APIs endpoint under the hood.
3434

35-
- [Serverless Framework: Deploy on Scaleway Functions](#serverless-framework-deploy-on-scaleway-functions)
35+
- [Scaleway Plugin for Serverless Framework](#scaleway-plugin-for-serverless-framework)
36+
- [Quick-start](#quick-start)
37+
- [Contents](#contents)
3638
- [Requirements](#requirements)
3739
- [Create a Project](#create-a-project)
3840
- [Configure your functions](#configure-your-functions)
@@ -341,7 +343,8 @@ You may refer to the follow examples:
341343

342344
Custom domains allows users to use their own domains.
343345

344-
For domain configuration please [Refer to Scaleway Documentation](https://www.scaleway.com/en/docs/compute/functions/how-to/add-a-custom-domain-name-to-a-function/)
346+
For domain configuration please refer to [custom domains on Function](https://www.scaleway.com/en/docs/compute/functions/how-to/add-a-custom-domain-name-to-a-function/) or
347+
[custom domains on Container](https://www.scaleway.com/en/docs/compute/containers/how-to/add-a-custom-domain-to-a-container/)
345348

346349
Integration with serverless framework example :
347350

deploy/lib/createContainers.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,84 @@ module.exports = {
2424
});
2525
},
2626

27+
applyDomains(containerId, customDomains) {
28+
// we make a diff to know which domains to add or delete
29+
const domainsToCreate = [];
30+
const domainsIdToDelete = [];
31+
const existingDomains = [];
32+
33+
this.listDomainsContainer(containerId).then((domains) => {
34+
domains.forEach((domain) => {
35+
existingDomains.push({ hostname: domain.hostname, id: domain.id });
36+
});
37+
38+
if (
39+
customDomains !== undefined &&
40+
customDomains !== null &&
41+
customDomains.length > 0
42+
) {
43+
customDomains.forEach((customDomain) => {
44+
domainsIdToDelete.push(customDomain.id);
45+
46+
let domainFound = false;
47+
48+
existingDomains.forEach((existingDom) => {
49+
if (existingDom.hostname === customDomain) {
50+
domainFound = true;
51+
return false;
52+
}
53+
});
54+
if (!domainFound) {
55+
domainsToCreate.push(customDomain);
56+
}
57+
});
58+
}
59+
60+
existingDomains.forEach((existingDomain) => {
61+
if (
62+
(customDomains === undefined || customDomains === null) &&
63+
existingDomain.id !== undefined
64+
) {
65+
domainsIdToDelete.push(existingDomain.id);
66+
} else if (!customDomains.includes(existingDomain.hostname)) {
67+
domainsIdToDelete.push(existingDomain.id);
68+
}
69+
});
70+
71+
domainsToCreate.forEach((newDomain) => {
72+
const createDomainParams = { container_id: containerId, hostname: newDomain };
73+
74+
this.createDomain(createDomainParams)
75+
.then((res) => {
76+
this.serverless.cli.log(`Creating domain ${res.hostname}`);
77+
})
78+
.then(
79+
() => {},
80+
(reason) => {
81+
this.serverless.cli.log(
82+
`Error on domain : ${newDomain}, reason : ${reason.message}`
83+
);
84+
85+
if (reason.message.includes("could not validate")) {
86+
this.serverless.cli.log(
87+
"Ensure CNAME configuration is ok, it can take some time for a record to propagate"
88+
);
89+
}
90+
}
91+
);
92+
});
93+
94+
domainsIdToDelete.forEach((domainId) => {
95+
if (domainId === undefined) {
96+
return;
97+
}
98+
this.deleteDomain(domainId).then((res) => {
99+
this.serverless.cli.log(`Deleting domain ${res.hostname}`);
100+
});
101+
});
102+
});
103+
},
104+
27105
createOrUpdateContainers(foundContainers) {
28106
const { containers } = this.provider.serverless.service.custom;
29107

@@ -67,6 +145,14 @@ module.exports = {
67145
port: container.port,
68146
};
69147

148+
// checking if there is custom_domains set on container creation.
149+
if (container.custom_domains && container.custom_domains.length > 0) {
150+
this.serverless.cli.log("WARNING: custom_domains are available on container update only. "+
151+
"Redeploy your container to apply custom domains. Doc : https://www.scaleway.com/en/docs/compute/containers/how-to/add-a-custom-domain-to-a-container/")
152+
}
153+
154+
this.serverless.cli.log(`Creating function ${func.name}...`);
155+
70156
this.serverless.cli.log(`Creating container ${container.name}...`);
71157

72158
return this.createContainer(params)
@@ -91,6 +177,10 @@ module.exports = {
91177
};
92178

93179
this.serverless.cli.log(`Updating container ${container.name}...`);
180+
181+
// assign domains
182+
this.applyDomains(foundContainer.id, container.custom_domains);
183+
94184
return this.updateContainer(foundContainer.id, params)
95185
.then(response => Object.assign(response, { directory: container.directory }));
96186
},

deploy/lib/createFunctions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module.exports = {
5959
const domainsIdToDelete = [];
6060
const existingDomains = [];
6161

62-
this.listDomains(funcId).then((domains) => {
62+
this.listDomainsFunction(funcId).then((domains) => {
6363
domains.forEach((domain) => {
6464
existingDomains.push({ hostname: domain.hostname, id: domain.id });
6565
});
@@ -201,7 +201,7 @@ module.exports = {
201201
params.runtime = this.validateRuntime(
202202
func,
203203
availableRuntimes,
204-
this.serverless.cli
204+
this.serverless.cli,
205205
);
206206

207207
// checking if there is custom_domains set on function creation.

deploy/lib/deployContainers.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,41 @@ const BbPromise = require('bluebird');
44

55
module.exports = {
66
deployContainers() {
7-
this.serverless.cli.log('Deploying Containers...');
7+
this.serverless.cli.log("Deploying Containers...");
88
return BbPromise.bind(this)
99
.then(this.deployEachContainer)
10-
.then(() => this.serverless.cli.log('Waiting for container deployments, this may take multiple minutes...'))
10+
.then(() =>
11+
this.serverless.cli.log(
12+
"Waiting for container deployments, this may take multiple minutes..."
13+
)
14+
)
1115
.then(this.printContainerEndpointsAfterDeployment);
1216
},
1317

1418
deployEachContainer() {
15-
const promises = this.containers.map(
16-
container => this.deployContainer(container.id),
19+
const promises = this.containers.map((container) =>
20+
this.deployContainer(container.id)
1721
);
1822
return Promise.all(promises);
1923
},
2024

2125
printContainerEndpointsAfterDeployment() {
22-
return this.waitContainersAreDeployed(this.namespace.id)
23-
.then(containers => containers.forEach(
24-
container => this.serverless.cli.log(`Container ${container.name} has been deployed to: https://${container.domain_name}`),
25-
));
26+
return this.waitContainersAreDeployed(this.namespace.id).then(
27+
(containers) => {
28+
containers.forEach((container) => {
29+
this.serverless.cli.log(
30+
`Container ${container.name} has been deployed to: https://${container.domain_name}`
31+
);
32+
33+
this.serverless.cli.log("Waiting for domains deployment...");
34+
35+
this.waitDomainsAreDeployedContainer(container.id).then((domains) => {
36+
domains.forEach((domain) => {
37+
this.serverless.cli.log(`Domain ready : ${domain.hostname}`);
38+
});
39+
});
40+
});
41+
}
42+
);
2643
},
2744
};

deploy/lib/deployFunctions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module.exports = {
4040
'Waiting for domains deployment...',
4141
);
4242

43-
this.waitDomainsAreDeployed(func.id)
43+
this.waitDomainsAreDeployedFunction(func.id)
4444
.then((domains) => {
4545
domains.forEach((domain) => {
4646
this.serverless.cli.log(`Domain ready : ${domain.hostname}`);

shared/api/domain.js

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ const { manageError } = require("./utils");
55
module.exports = {
66
/**
77
* listDomains is used to read all domains of a wanted function.
8-
* @param {Number} functionId the id of the function to read domains.
8+
* @param {Number} functionId the id of the function to read domains.
99
* @returns a Promise with request result.
1010
*/
11-
listDomains(functionId) {
11+
listDomainsFunction(functionId) {
1212
const domainsUrl = `domains?function_id=${functionId}`;
1313

1414
return this.apiManager
@@ -17,6 +17,20 @@ module.exports = {
1717
.catch(manageError);
1818
},
1919

20+
/**
21+
* listDomains is used to read all domains of a wanted container.
22+
* @param {Number} containerId the id of the container to read domains.
23+
* @returns a Promise with request result.
24+
*/
25+
listDomainsContainer(containerId) {
26+
const domainsUrl = `domains?container_id=${containerId}`;
27+
28+
return this.apiManager
29+
.get(domainsUrl)
30+
.then((response) => response.data.domains)
31+
.catch(manageError);
32+
},
33+
2034
/**
2135
* createDomain is used to call for domain creation, warning : this
2236
* function does not wait for the domain
@@ -48,29 +62,64 @@ module.exports = {
4862

4963
/**
5064
* Waiting for all domains to be ready on a function
51-
* @param {Number} functionId
65+
* @param {UUID} functionId
5266
* @returns
5367
*/
54-
waitDomainsAreDeployed(functionId) {
68+
waitDomainsAreDeployedFunction(functionId) {
5569
return this.listDomains(functionId)
5670
.then((domains) => {
57-
let domainssAreReady = true;
71+
let domainsAreReady = true;
72+
73+
for (let i = 0; i < domains.length; i += 1) {
74+
const domain = domains[i];
75+
76+
if (domain.status === 'error') {
77+
throw new Error(domain.error_message);
78+
}
79+
80+
if (domain.status !== 'ready') {
81+
domainsAreReady = false;
82+
break;
83+
}
84+
}
85+
if (!domainsAreReady) {
86+
return new Promise((resolve) => {
87+
setTimeout(() => resolve(this.waitDomainsAreDeployedFunction(functionId)), 5000);
88+
});
89+
}
90+
return domains;
91+
});
92+
},
93+
94+
/**
95+
* Waiting for all domains to be ready on a container
96+
* @param {UUID} containerId
97+
* @returns
98+
*/
99+
waitDomainsAreDeployedContainer(containerId) {
100+
return this.listDomains(containerId)
101+
.then((domains) => {
102+
let domainsAreReady = true;
103+
58104
for (let i = 0; i < domains.length; i += 1) {
59105
const domain = domains[i];
106+
60107
if (domain.status === 'error') {
61108
throw new Error(domain.error_message);
62109
}
110+
63111
if (domain.status !== 'ready') {
64-
domainssAreReady = false;
112+
domainsAreReady = false;
65113
break;
66114
}
67115
}
68-
if (!domainssAreReady) {
116+
if (!domainsAreReady) {
69117
return new Promise((resolve) => {
70-
setTimeout(() => resolve(this.waitDomainsAreDeployed(functionId)), 5000);
118+
setTimeout(() => resolve(this.waitDomainsAreDeployedContainer(containerId)), 5000);
71119
});
72120
}
73121
return domains;
74122
});
75123
},
124+
76125
};

0 commit comments

Comments
 (0)