Compare commits
10 Commits
b11e5144e3
...
714c4f6ed4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
714c4f6ed4 | ||
|
|
3d00a07e41 | ||
|
|
9b25fa2eda | ||
|
|
dae7fbc9e7 | ||
|
|
519b3378b1 | ||
|
|
b50c7e9ddc | ||
|
|
e359485d94 | ||
|
|
89bd55c83b | ||
|
|
e546e17c7e | ||
|
|
ecd004e2ca |
34
.gitignore
vendored
34
.gitignore
vendored
@@ -1 +1,33 @@
|
|||||||
data
|
app/dataDir/*
|
||||||
|
|
||||||
|
# Local .terraform directories
|
||||||
|
**/.terraform/*
|
||||||
|
|
||||||
|
# .tfstate files
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.*
|
||||||
|
|
||||||
|
# Crash log files
|
||||||
|
crash.log
|
||||||
|
|
||||||
|
# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
|
||||||
|
# .tfvars files are managed as part of configuration and so should be included in
|
||||||
|
# version control.
|
||||||
|
#
|
||||||
|
# example.tfvars
|
||||||
|
|
||||||
|
# Ignore override files as they are usually used to override resources locally and so
|
||||||
|
# are not checked in
|
||||||
|
override.tf
|
||||||
|
override.tf.json
|
||||||
|
*_override.tf
|
||||||
|
*_override.tf.json
|
||||||
|
|
||||||
|
# Include override files you do wish to add to version control using negated pattern
|
||||||
|
#
|
||||||
|
# !example_override.tf
|
||||||
|
|
||||||
|
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||||
|
# example: *tfplan*
|
||||||
|
|
||||||
|
dumbserver.image.tar
|
||||||
|
|||||||
37
README
Normal file
37
README
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
I am taking this as a test project for docker and terraform too...
|
||||||
|
The Terraform part is on the more "enterprise" side with not only just a public IP on the EC2.
|
||||||
|
|
||||||
|
for the docker imaging look at: https://www.fosstechnix.com/how-to-create-docker-image-for-node-js-application/
|
||||||
|
The docker image can be built with
|
||||||
|
podman build -t dumbserver .
|
||||||
|
|
||||||
|
the image then can be extracted with
|
||||||
|
podman image save -o dumbserver.image.tar dumbserver
|
||||||
|
|
||||||
|
on the target machine it can be imported with
|
||||||
|
podman image load -i dumbserer.image.tar
|
||||||
|
|
||||||
|
and running a container with the image id done via:
|
||||||
|
podman run --name dumbserver -p 1280:1280 dumbserver
|
||||||
|
(Port 1280 is what it listens on)
|
||||||
|
|
||||||
|
updating the image from a local file (in podman):
|
||||||
|
podman pull oci-archive:/tmp/dumbserver.image.tar
|
||||||
|
then the container needs to be re-created... stop/rm/run
|
||||||
|
|
||||||
|
At this stage, terraform will create an ssh accessable server (key is in "terraformtest1.ppk") - tutorial used for that: https://medium.com/@hmalgewatta/setting-up-an-aws-ec2-instance-with-ssh-access-using-terraform-c336c812322f
|
||||||
|
|
||||||
|
As part of the terraform, I copy a script to install docker/podman - and upload the image
|
||||||
|
then I set up the container and start it. that's it.
|
||||||
|
|
||||||
|
initialize with
|
||||||
|
terraform init
|
||||||
|
|
||||||
|
then plan
|
||||||
|
terraform plan
|
||||||
|
|
||||||
|
then apply
|
||||||
|
terraform apply
|
||||||
|
|
||||||
|
To read the terraform output into powershell use
|
||||||
|
$Json_Output = (& terraform output -json) | ConvertFrom-Json
|
||||||
18
app/Dockerfile
Normal file
18
app/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
FROM node:current-slim
|
||||||
|
|
||||||
|
# To Create nodejsapp directory
|
||||||
|
WORKDIR /nodejsapp
|
||||||
|
|
||||||
|
# To Install All dependencies
|
||||||
|
COPY package*.json .
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# To copy all application packages
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# expose the data dir as a volume
|
||||||
|
VOLUME [ "dataDir" ]
|
||||||
|
|
||||||
|
# Expose port 1280 and Run the server.js file to start node js application
|
||||||
|
EXPOSE 1280
|
||||||
|
CMD [ "node", "dumbserver.js" ]
|
||||||
233
app/dumbserver.js
Normal file
233
app/dumbserver.js
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
//
|
||||||
|
// Dependencies
|
||||||
|
//
|
||||||
|
let http = require("http");
|
||||||
|
let fs = require("fs");
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constants
|
||||||
|
//
|
||||||
|
let DATA_LOCATION = "dataDir/data"; // both the url root and the location of data file
|
||||||
|
let DATA_LOCATION_LEN = DATA_LOCATION.length;
|
||||||
|
let DATA_FILE = __dirname + "/" + DATA_LOCATION;
|
||||||
|
let PORT = 1280;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Load store file
|
||||||
|
//
|
||||||
|
let store = {}; // deafult empty object
|
||||||
|
let json = "";
|
||||||
|
try {
|
||||||
|
let json = fs.readFileSync(DATA_FILE);
|
||||||
|
store = JSON.parse(json);
|
||||||
|
console.log("Read store file");
|
||||||
|
} catch (e) {
|
||||||
|
// lazy error handling
|
||||||
|
console.log("Could not load store file. Using empty array.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Flush to store file on exit
|
||||||
|
//
|
||||||
|
let flush = function () {
|
||||||
|
console.log("Flushing data to file [" + DATA_FILE + "]\n");
|
||||||
|
fs.writeFileSync(DATA_FILE, JSON.stringify(store));
|
||||||
|
};
|
||||||
|
|
||||||
|
let handleInterrupt = function () {
|
||||||
|
flush();
|
||||||
|
process.exit();
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on("SIGINT", handleInterrupt);
|
||||||
|
process.on("SIGTERM", handleInterrupt);
|
||||||
|
process.on("SIGHUP", flush); // HUP flushes to file but keeps the server running
|
||||||
|
|
||||||
|
//
|
||||||
|
// If request URI is prefixed with the value of DATA_LOCATION, we treat incoming request as a REST request
|
||||||
|
// Otherwise we serve the file referenced by the URI
|
||||||
|
//
|
||||||
|
let serveFile = function (loc, req, resp) {
|
||||||
|
let fullPath = __dirname + "/" + loc;
|
||||||
|
console.log("Trying to serve file at [" + fullPath + "]");
|
||||||
|
|
||||||
|
if (req.method !== "GET") {
|
||||||
|
// read only!
|
||||||
|
resp.writeHead(405);
|
||||||
|
resp.write("Unsupported method\n");
|
||||||
|
resp.end();
|
||||||
|
} else if (fs.existsSync(fullPath)) {
|
||||||
|
resp.writeHead(200);
|
||||||
|
fs.createReadStream(fullPath).pipe(resp);
|
||||||
|
} else {
|
||||||
|
resp.writeHead(404);
|
||||||
|
resp.write("Not Found\n");
|
||||||
|
resp.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let serveRestData = function (urlPath, req, resp) {
|
||||||
|
console.log(req.method + " request for path [" + urlPath + "]");
|
||||||
|
|
||||||
|
switch (req.method) {
|
||||||
|
case "GET":
|
||||||
|
let exactMatch = !urlPath.endsWith("/");
|
||||||
|
let matchFound = false;
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
// assumes that only a single resource has this ID.
|
||||||
|
console.log(" - Exact Match.");
|
||||||
|
if (store.hasOwnProperty(urlPath)) {
|
||||||
|
resp.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
resp.write(JSON.stringify(store[urlPath]) + "\n");
|
||||||
|
matchFound = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(" - All Matches.");
|
||||||
|
let matches = [];
|
||||||
|
Object.keys(store).forEach((theKey) => {
|
||||||
|
if (theKey.startsWith(urlPath)) {
|
||||||
|
matches.push(store[theKey]);
|
||||||
|
matchFound = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchFound) {
|
||||||
|
resp.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
resp.write(JSON.stringify(matches));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchFound) {
|
||||||
|
console.log(" - Match NOT found.");
|
||||||
|
resp.writeHead(404, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"Not Found"}');
|
||||||
|
} else {
|
||||||
|
console.log(" - Match found.");
|
||||||
|
}
|
||||||
|
resp.end();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PUT":
|
||||||
|
json = "";
|
||||||
|
req.on("data", function (chunk) {
|
||||||
|
json += chunk;
|
||||||
|
});
|
||||||
|
req.on("end", function () {
|
||||||
|
try {
|
||||||
|
let data = JSON.parse(json);
|
||||||
|
store[urlPath] = data;
|
||||||
|
resp.writeHead(201, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":true}');
|
||||||
|
} catch (e) {
|
||||||
|
resp.writeHead(400, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"Invalid JSON"}');
|
||||||
|
}
|
||||||
|
resp.end();
|
||||||
|
flush();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "POST":
|
||||||
|
json = "";
|
||||||
|
req.on("data", function (chunk) {
|
||||||
|
json += chunk;
|
||||||
|
});
|
||||||
|
req.on("end", function () {
|
||||||
|
try {
|
||||||
|
let data = JSON.parse(json);
|
||||||
|
if (data.ID === undefined) {
|
||||||
|
resp.writeHead(400, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"The ID Field needs to be given"}');
|
||||||
|
} else {
|
||||||
|
// make sure the ID from the data is used as the final resource Identifier,
|
||||||
|
// even if the URL has none or says different.
|
||||||
|
let re = new RegExp("/[^/]*$");
|
||||||
|
urlPath = urlPath.replace(re, "");
|
||||||
|
urlPath = urlPath + "/" + data.ID;
|
||||||
|
console.log(" - storing with resource key: [" + urlPath + "]");
|
||||||
|
|
||||||
|
store[urlPath] = data;
|
||||||
|
|
||||||
|
resp.writeHead(201, { "Content-Type": "application/json" });
|
||||||
|
resp.write(JSON.stringify(store[urlPath]));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
resp.writeHead(400, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"Invalid JSON"}');
|
||||||
|
}
|
||||||
|
resp.end();
|
||||||
|
flush();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PATCH":
|
||||||
|
json = "";
|
||||||
|
req.on("data", function (chunk) {
|
||||||
|
json += chunk;
|
||||||
|
});
|
||||||
|
req.on("end", function () {
|
||||||
|
try {
|
||||||
|
let data = JSON.parse(json);
|
||||||
|
Object.keys(data).forEach((theKey) => {
|
||||||
|
store[urlPath][theKey] = data[theKey];
|
||||||
|
});
|
||||||
|
|
||||||
|
resp.writeHead(201, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":true}');
|
||||||
|
} catch (e) {
|
||||||
|
resp.writeHead(400, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"Invalid JSON"}');
|
||||||
|
}
|
||||||
|
resp.end();
|
||||||
|
flush();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "OPTIONS":
|
||||||
|
resp.end();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DELETE":
|
||||||
|
if (store.hasOwnProperty(urlPath)) {
|
||||||
|
delete store[urlPath];
|
||||||
|
resp.writeHead(204, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":true}');
|
||||||
|
} else {
|
||||||
|
resp.writeHead(404, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"Not Found"}');
|
||||||
|
}
|
||||||
|
resp.end();
|
||||||
|
flush();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
resp.writeHead(405, { "Content-Type": "application/json" });
|
||||||
|
resp.write('{"success":false, "error":"Unsupported method"}');
|
||||||
|
resp.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listen for connections
|
||||||
|
//
|
||||||
|
http
|
||||||
|
.createServer(function (req, resp) {
|
||||||
|
// CORS headers
|
||||||
|
resp.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
resp.setHeader("Access-Control-Allow-Headers", "X-Request-With, content-type");
|
||||||
|
resp.setHeader("Vary", "Origin");
|
||||||
|
|
||||||
|
resp.setHeader("Access-Control-Allow-Methods", "GET, PATCH, POST, PUT, DELETE, OPTIONS");
|
||||||
|
|
||||||
|
console.log("Serving [" + req.url + "]");
|
||||||
|
|
||||||
|
if (req.url === "/") {
|
||||||
|
// server index file
|
||||||
|
serveFile("index.html", req, resp);
|
||||||
|
} else {
|
||||||
|
// rest request
|
||||||
|
serveRestData(req.url, req, resp);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.listen(PORT);
|
||||||
9
app/index.html
Normal file
9
app/index.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
Dumbserver
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Server version: 2022-09-30_10_21_17</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
app/package.json
Normal file
19
app/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "dumbserver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "dumbserver.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/noelbennett/dumbserver.git"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/noelbennett/dumbserver/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/noelbennett/dumbserver#readme"
|
||||||
|
}
|
||||||
23
docker/buildimage.cmd
Normal file
23
docker/buildimage.cmd
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
docker build -t dumbserver ../app/
|
||||||
|
docker image save -o dumbserver.image.tar dumbserver
|
||||||
|
|
||||||
|
# [root@podhost tmp]# podman image load --input dumbserver.image.tar
|
||||||
|
# Getting image source signatures
|
||||||
|
# Copying blob b45078e74ec9 done
|
||||||
|
# Copying blob b34a262752ce done
|
||||||
|
# Copying blob 06cdaeea8c1e done
|
||||||
|
# Copying blob 192b450771fa done
|
||||||
|
# Copying blob 273c8cbf7fe9 done
|
||||||
|
# Copying blob 222ecd8c2394 done
|
||||||
|
# Copying blob b243f76c651c done
|
||||||
|
# Copying blob 7c655cb7234e done
|
||||||
|
# Copying blob ef546e9fb70f done
|
||||||
|
# Copying config e9ffb6042a done
|
||||||
|
# Writing manifest to image destination
|
||||||
|
# Storing signatures
|
||||||
|
# Loaded image: localhost/dumbserver:latest
|
||||||
|
# [root@podhost tmp]# podman image ls
|
||||||
|
# REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
# localhost/dumbserver latest e9ffb6042ab0 4 months ago 248 MB
|
||||||
|
# docker.io/nodered/node-red latest a15fc0f4e930 6 months ago 492 MB
|
||||||
|
# [root@podhost tmp]# podman run -d -v /home/podman/dubserverData:/nodejsapp/dataDir -p 1280:1280 --name=dumbserver dumbserver
|
||||||
166
dumbserver.js
166
dumbserver.js
@@ -1,166 +0,0 @@
|
|||||||
//
|
|
||||||
// Dependencies
|
|
||||||
//
|
|
||||||
let http = require('http');
|
|
||||||
let fs = require('fs');
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constants
|
|
||||||
//
|
|
||||||
let DATA_LOCATION = 'data'; // both the url root and the location of data file
|
|
||||||
let DATA_LOCATION_LEN = DATA_LOCATION.length;
|
|
||||||
let DATA_FILE = __dirname + '/' + DATA_LOCATION;
|
|
||||||
let PORT = 1280;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Load store file
|
|
||||||
//
|
|
||||||
let store = {}; // deafult empty object
|
|
||||||
try {
|
|
||||||
let json = fs.readFileSync(DATA_FILE);
|
|
||||||
store = JSON.parse(json);
|
|
||||||
console.log('Read store file');
|
|
||||||
} catch (e) { // lazy error handling
|
|
||||||
console.log('Could not load store file. Using empty array.');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Flush to store file on exit
|
|
||||||
//
|
|
||||||
let flush = function()
|
|
||||||
{
|
|
||||||
console.log('Caught deadly signal.');
|
|
||||||
console.log('Flushing data to file [' + DATA_FILE + ']\n');
|
|
||||||
fs.writeFileSync(DATA_FILE, JSON.stringify(store));
|
|
||||||
};
|
|
||||||
|
|
||||||
let handleInterrupt = function() { flush(); process.exit(); };
|
|
||||||
|
|
||||||
process.on('SIGINT', handleInterrupt);
|
|
||||||
process.on('SIGTERM', handleInterrupt);
|
|
||||||
process.on('SIGHUP', flush); // HUP flushes to file but keeps the server running
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// If request URI is prefixed with the value of DATA_LOCATION, we treat incoming request as a REST request
|
|
||||||
// Otherwise we serve the file referenced by the URI
|
|
||||||
//
|
|
||||||
let serveFile = function(loc, req, resp) {
|
|
||||||
let fullPath = __dirname + '/' + loc;
|
|
||||||
console.log('Trying to serve file at [' + fullPath + ']');
|
|
||||||
|
|
||||||
if (req.method !== 'GET') { // read only!
|
|
||||||
resp.writeHead(405);
|
|
||||||
resp.write('Unsupported method\n');
|
|
||||||
resp.end();
|
|
||||||
} else if (fs.existsSync(fullPath)) {
|
|
||||||
resp.writeHead(200);
|
|
||||||
fs.createReadStream(fullPath).pipe(resp);
|
|
||||||
} else {
|
|
||||||
resp.writeHead(404);
|
|
||||||
resp.write('Not Found\n');
|
|
||||||
resp.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let serveRestData = function(key, req, resp) {
|
|
||||||
console.log(req.method + ' request for key [' + key + ']');
|
|
||||||
|
|
||||||
switch (req.method) {
|
|
||||||
|
|
||||||
case 'GET':
|
|
||||||
let exactMatch=!key.endsWith('/');
|
|
||||||
let matchFound=false;
|
|
||||||
|
|
||||||
if (exactMatch) {
|
|
||||||
console.log(" - Exact Match.");
|
|
||||||
if (store.hasOwnProperty(key)) {
|
|
||||||
resp.writeHead(200, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write(JSON.stringify(store[key]) + '\n');
|
|
||||||
matchFound=true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(" - All Matches.");
|
|
||||||
let matches=[];
|
|
||||||
Object.keys(store).forEach(theKey=>{
|
|
||||||
if (theKey.startsWith(key)){
|
|
||||||
matches.push(store[theKey]);
|
|
||||||
matchFound=true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchFound) {
|
|
||||||
resp.writeHead(200, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write(JSON.stringify(matches));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!matchFound) {
|
|
||||||
console.log(" - Match NOT found.");
|
|
||||||
resp.writeHead(404, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write('{"success":false, "error":"Not Found"}');
|
|
||||||
} else {
|
|
||||||
console.log(" - Match not found.");
|
|
||||||
}
|
|
||||||
resp.end();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'PUT':
|
|
||||||
case 'POST':
|
|
||||||
let json = '';
|
|
||||||
req.on('data', function(chunk) { json += chunk; });
|
|
||||||
req.on('end', function() {
|
|
||||||
try {
|
|
||||||
let data = JSON.parse(json);
|
|
||||||
store[key] = data;
|
|
||||||
resp.writeHead(201, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write('{"success":true}');
|
|
||||||
} catch (e) {
|
|
||||||
resp.writeHead(400, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write('{"success":false, "error":"Invalid JSON"}');
|
|
||||||
}
|
|
||||||
resp.end();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'DELETE':
|
|
||||||
if (store.hasOwnProperty(key)) {
|
|
||||||
delete store[key];
|
|
||||||
resp.writeHead(204, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write('{"success":true}');
|
|
||||||
} else {
|
|
||||||
resp.writeHead(404, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write('{"success":false, "error":"Not Found"}');
|
|
||||||
}
|
|
||||||
resp.end();
|
|
||||||
flush();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
resp.writeHead(405, { 'Content-Type' : 'application/json' });
|
|
||||||
resp.write('{"success":false, "error":"Unsupported method"}');
|
|
||||||
resp.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listen for connections
|
|
||||||
//
|
|
||||||
http.createServer(function(req, resp) {
|
|
||||||
// CORS headers
|
|
||||||
resp.setHeader('Access-Control-Allow-Origin', '*');
|
|
||||||
resp.setHeader('Vary', 'Origin');
|
|
||||||
|
|
||||||
let uri = req.url //.replace(/^\/+|\/+$/g, ''); // trim slashes
|
|
||||||
console.log('Serving [' + req.url + ']');
|
|
||||||
|
|
||||||
if (uri === '/') { // server index file
|
|
||||||
uri = 'index.html';
|
|
||||||
serveFile(uri, req, resp);
|
|
||||||
} else { // rest request
|
|
||||||
serveRestData(req.url, req, resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
}).listen(PORT);
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Hello World!</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
26
teraformtest1.ppk
Normal file
26
teraformtest1.ppk
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
PuTTY-User-Key-File-2: ssh-rsa
|
||||||
|
Encryption: none
|
||||||
|
Comment: teraformtest1
|
||||||
|
Public-Lines: 6
|
||||||
|
AAAAB3NzaC1yc2EAAAADAQABAAABAQChxclCCpzzSqjbocO5s5/Pm0XFWDqxenuA
|
||||||
|
XEDxxBz2Y8kKeIZeaf6YKMWBYUM+SFBNH8Y5VkHoBPggTWohKe/Yxwwj/abhEZzZ
|
||||||
|
pSEzhGUm2xii4Qwc/MRyGNe6d99caXJJy+CJQztMkJdzwpY3r384HBmdy+SRmW2S
|
||||||
|
+Isz0FOMfF+eIZf63ifU3RQKldTKl+vzOlugRj0AROxkF3bvRDwhpCYwuCrtK+hC
|
||||||
|
rSdRoVcgQVp97lvXbl5GM+bCBg/TNGP05dIs2eZoFPeVgh74TCU53yab7RPVmgEj
|
||||||
|
oXMGx/pLBtmua6uYsQXt2tLnnPIwaTOI+NLRrycTa89xM2jU3Vv/
|
||||||
|
Private-Lines: 14
|
||||||
|
AAABAQCOY1xluJm0ur4tsxBnX2cGgJDExofCtyAFNy4ino8vf1zwzmzWpUzl+Nz+
|
||||||
|
p/Fb3KJxf8olpXqVEeqj4//J+POXRnu1IgnEiJAaMXIruhiePrJMivDygrkWBC+9
|
||||||
|
fM0otddWhRC5QGftWEc2KMu3b8z2QiV58oDYdscvWXyMuo0PVPJhjgHcTZVMbpYW
|
||||||
|
ISWckZQUMQRV/rAZVAegLDYsQP212KogFPQlzaHJRFIvxf6OZkE7bvvIMSARXEGu
|
||||||
|
OeKru38n34vk7Tp7f6+f8KtGYCkyjYyhImThfA1ne/SjEM9CRIf+AH2jhKyB+C8D
|
||||||
|
o4tsf5DVnC6orbdnt4bT0gwjaeihAAAAgQDaY5BCPLD+w3Q8TRZbGw73AsA8tPss
|
||||||
|
1Qv5p044iFXu2PkL/YtaMrMYwRn9IqasjvM0BJlNJDfEmUu67jsa+nWgaKlVWmYz
|
||||||
|
Em4WYK8CFkWr5wZLUqztemKYfgKT7vEmPLQun3HS9P7+AyyiX71YBEXucJu7HsIc
|
||||||
|
I46tTza842XtUwAAAIEAvaIYUqlbHOqxdFyqMAH6daH5KzCneF9S3JmVt6U52GAE
|
||||||
|
8d1Cj3XRRSKTPPt66dDaewlVqhqPjcnSxET/n0+VnrKal2UBrslwFWF8szpX6+Zo
|
||||||
|
B3n3Upg1MdlTaFrmPpJGDehT9NjerUOLFvgEvkwdCHedoyLeIXm6zsFJ3/Mk1SUA
|
||||||
|
AACBAMgeyHq/4Nur+/DY4i5IrlQUX6gpQF2hb1xj8WElaBQa+4IgHWeApDAycIc1
|
||||||
|
D1pLXKHClacSpKqv/6gHlz4KkGsnRUe3bpm+fP0XjIjTPp4l4s32TCqY9X3Eu5fL
|
||||||
|
UaRqrMtlJ19TMD8rgD1GkVVzBOJXsUHUwTcCg0JhY1DNuC4Y
|
||||||
|
Private-MAC: 1e430a894b1ac27e8e67f633c823792c078574ed
|
||||||
22
terraform/.terraform.lock.hcl
generated
Normal file
22
terraform/.terraform.lock.hcl
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# This file is maintained automatically by "terraform init".
|
||||||
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
|
provider "registry.terraform.io/hashicorp/aws" {
|
||||||
|
version = "4.32.0"
|
||||||
|
constraints = "~> 4.16"
|
||||||
|
hashes = [
|
||||||
|
"h1:CmNkbcfctajxal8fnFFWLSC5dDvQwSJrw7HiwpTdyUc=",
|
||||||
|
"zh:062c30cd8bcf29f8ee34c2b2509e4e8695c2bcac8b7a8145e1c72e83d4e68b13",
|
||||||
|
"zh:1503fabaace96a7eea4d73ced36a02a75ec587760850e58162e7eff419dcbb31",
|
||||||
|
"zh:39a1fa36f8cb999f048bf0000d9dab40b8b0c77df35584fb08aa8bd6c5052dee",
|
||||||
|
"zh:471a755d43b51cd7be3e386cebc151ad8d548c5dea798343620476887e721882",
|
||||||
|
"zh:61ed56fab811e62b8286e606d003f7eeb7e940ef99bb49c1d283d91c0b748cc7",
|
||||||
|
"zh:80607dfe5f7770d136d5c451308b9861084ffad08139de8014e48672ec43ea3f",
|
||||||
|
"zh:863bf0a6576f7a969a89631525250d947fbb207d3d13e7ca4f74d86bd97cdda3",
|
||||||
|
"zh:9a8f2e77e4f99dbb618eb8ad17218a4698833754b50d46da5727323a2050a400",
|
||||||
|
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||||
|
"zh:9b74ff6e638c2a470b3599d57c2081e0095976da0a54b6590884d571f930b53b",
|
||||||
|
"zh:da4fc553d50ae833d860ec95120e271c29b4cb636917ab5991327362b7486bb7",
|
||||||
|
"zh:f4b86e7df4e846a38774e8e648b41c5ebaddcefa913cfa1864568086b7735575",
|
||||||
|
]
|
||||||
|
}
|
||||||
64
terraform/appServer.tf
Normal file
64
terraform/appServer.tf
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 4.16"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required_version = ">= 1.2.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
region = "us-west-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "app_server" {
|
||||||
|
ami = var.ami_id
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
key_name = var.ami_key_pair_name
|
||||||
|
security_groups = ["${aws_security_group.ingress-all-test.id}"]
|
||||||
|
|
||||||
|
# this is probably the only way to get the remote-execution (below) working. The elastic IP is not avaialable at this time
|
||||||
|
associate_public_ip_address = true
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
"Name" = var.ami_name
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet_id = "${aws_subnet.subnet-uno.id}"
|
||||||
|
|
||||||
|
connection {
|
||||||
|
type="ssh"
|
||||||
|
user="ubuntu"
|
||||||
|
host = self.public_ip
|
||||||
|
# we are assuming the ppk key is loaded into pageant...
|
||||||
|
agent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upload the set up script
|
||||||
|
provisioner "file" {
|
||||||
|
source = "setupAppServer.sh"
|
||||||
|
destination = "/tmp/setupAppServer.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Uplaod docker image
|
||||||
|
provisioner "file" {
|
||||||
|
source = "../docker/dumbserver.image.tar"
|
||||||
|
destination = "/tmp/dumbserver.image.tar"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upload example data file
|
||||||
|
provisioner "file" {
|
||||||
|
source = "../app/dataDir/data"
|
||||||
|
destination = "/tmp/data"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "remote-exec" {
|
||||||
|
inline = [
|
||||||
|
"chmod +x /tmp/setupAppServer.sh",
|
||||||
|
"sudo /tmp/setupAppServer.sh"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
terraform/gateways.tf
Normal file
7
terraform/gateways.tf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
resource "aws_internet_gateway" "test-env-gw" {
|
||||||
|
tags = {
|
||||||
|
"Name" = "tf-gateway-test-env"
|
||||||
|
}
|
||||||
|
|
||||||
|
vpc_id = "${aws_vpc.test-env.id}"
|
||||||
|
}
|
||||||
18
terraform/network.tf
Normal file
18
terraform/network.tf
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
resource "aws_vpc" "test-env" {
|
||||||
|
tags = {
|
||||||
|
"Name" = "tf-vpc-test-env"
|
||||||
|
}
|
||||||
|
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
enable_dns_hostnames = true
|
||||||
|
enable_dns_support = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_eip" "ip-test-env" {
|
||||||
|
tags = {
|
||||||
|
"Name" = "tf-EIP-IP-test-env"
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = "${aws_instance.app_server.id}"
|
||||||
|
vpc = true
|
||||||
|
}
|
||||||
9
terraform/outputs.tf
Normal file
9
terraform/outputs.tf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
output "instance_public_ip" {
|
||||||
|
description = "Public IP address of the EC2 instance"
|
||||||
|
value = aws_instance.app_server.public_ip
|
||||||
|
}
|
||||||
|
|
||||||
|
output "elastic_ip" {
|
||||||
|
description = "ELASTIC IP"
|
||||||
|
value = aws_eip.ip-test-env.public_ip
|
||||||
|
}
|
||||||
30
terraform/security.tf
Normal file
30
terraform/security.tf
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
resource "aws_security_group" "ingress-all-test" {
|
||||||
|
name = "allow-all-sg"
|
||||||
|
tags = {
|
||||||
|
"Name" = "tf-allow-all-sg"
|
||||||
|
}
|
||||||
|
vpc_id = "${aws_vpc.test-env.id}"
|
||||||
|
ingress {
|
||||||
|
cidr_blocks = [
|
||||||
|
"0.0.0.0/0"
|
||||||
|
]
|
||||||
|
from_port = 22
|
||||||
|
to_port = 22
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
ingress {
|
||||||
|
cidr_blocks = [
|
||||||
|
"0.0.0.0/0"
|
||||||
|
]
|
||||||
|
from_port = 1280
|
||||||
|
to_port = 1280
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
// Terraform removes the default rule
|
||||||
|
egress {
|
||||||
|
from_port = 0
|
||||||
|
to_port = 0
|
||||||
|
protocol = "-1"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
25
terraform/setupAppServer.sh
Normal file
25
terraform/setupAppServer.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
apt -y update
|
||||||
|
apt -y upgrade
|
||||||
|
apt -y update
|
||||||
|
apt -y install podman
|
||||||
|
|
||||||
|
podman image load -i /tmp/dumbserver.image.tar
|
||||||
|
|
||||||
|
# create the directory for the data file and, if not existing (which is the default) copy the example file
|
||||||
|
mkdir -p /var/local/dumbserver
|
||||||
|
if [ ! -f /var/local/dumbserver/data ]; then
|
||||||
|
cp /tmp/data /var/local/dumbserver/
|
||||||
|
fi
|
||||||
|
|
||||||
|
podman run --name dumbserver -d -p 1280:1280 -v /var/local/dumbserver:/nodejsapp/dataDir dumbserver
|
||||||
|
|
||||||
|
# make sure it comes up after reboot...
|
||||||
|
podman generate systemd --new --name dumbserver > /etc/systemd/system/dumbserver-podman.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable dumbserver-podman.service
|
||||||
|
|
||||||
|
# finally, all is set up and after all the updates we want to reboot into the new kernel version
|
||||||
|
shutdown -r 1
|
||||||
25
terraform/subnets.tf
Normal file
25
terraform/subnets.tf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
resource "aws_subnet" "subnet-uno" {
|
||||||
|
tags = {
|
||||||
|
"Name" = "tf-subnet-uno"
|
||||||
|
}
|
||||||
|
|
||||||
|
cidr_block = "${cidrsubnet(aws_vpc.test-env.cidr_block, 3, 1)}"
|
||||||
|
vpc_id = "${aws_vpc.test-env.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_route_table" "route-table-test-env" {
|
||||||
|
tags = {
|
||||||
|
"Name" = "tf-route-table-test-env"
|
||||||
|
}
|
||||||
|
|
||||||
|
vpc_id = "${aws_vpc.test-env.id}"
|
||||||
|
route {
|
||||||
|
cidr_block = "0.0.0.0/0"
|
||||||
|
gateway_id = "${aws_internet_gateway.test-env-gw.id}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "aws_route_table_association" "subnet-association" {
|
||||||
|
subnet_id = "${aws_subnet.subnet-uno.id}"
|
||||||
|
route_table_id = "${aws_route_table.route-table-test-env.id}"
|
||||||
|
}
|
||||||
13
terraform/variables.tf
Normal file
13
terraform/variables.tf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
variable "ami_id" {
|
||||||
|
type = string
|
||||||
|
default = "ami-017fecd1353bcc96e"
|
||||||
|
}
|
||||||
|
variable "ami_key_pair_name" {
|
||||||
|
type=string
|
||||||
|
default = "teraformtest1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ami_name" {
|
||||||
|
type = string
|
||||||
|
default = "dumbApplicationServer"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user