Adding some docker and terraform to it ... wip

This commit is contained in:
Joe Tretter
2022-09-29 13:39:46 -05:00
parent e359485d94
commit b50c7e9ddc
15 changed files with 428 additions and 182 deletions

33
.gitignore vendored
View File

@@ -1 +1,32 @@
data
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*

22
.terraform.lock.hcl generated Normal file
View 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",
]
}

15
Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
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 port 1280 and Run the server.js file to start node js application
EXPOSE 1280
CMD [ "node", "dumbserver.js" ]

19
README Normal file
View File

@@ -0,0 +1,19 @@
I am taking this as a test project for docker and terraform too...
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
docker build -t dumbserver .
the image then can be extracted with
docker image save -o dumbserver.image.tar dumbserver
on the target machine it can be imported with
docker image load -i dumbserer.image.tar
and running a container with the image id done via:
docker run -p 1280:1280 dumbserver
(Port 1280 is what it listens on)
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
what is required next is to install docker on the server and deploy the image.

BIN
ddd Normal file

Binary file not shown.

View File

@@ -1,89 +1,91 @@
//
// Dependencies
//
let http = require('http');
let fs = require('fs');
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 = "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 DATA_FILE = __dirname + "/" + DATA_LOCATION;
let PORT = 1280;
//
// Load store file
//
let store = {}; // deafult empty object
let json='';
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.');
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');
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
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 + ']');
let fullPath = __dirname + "/" + loc;
console.log("Trying to serve file at [" + fullPath + "]");
if (req.method !== 'GET') { // read only!
if (req.method !== "GET") {
// read only!
resp.writeHead(405);
resp.write('Unsupported method\n');
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.write("Not Found\n");
resp.end();
}
};
let serveRestData = function (urlPath, req, resp) {
console.log(req.method + ' request for path [' + urlPath + ']');
console.log(req.method + " request for path [" + urlPath + "]");
switch (req.method) {
case 'GET':
let exactMatch=!urlPath.endsWith('/');
case "GET":
let exactMatch = !urlPath.endsWith("/");
let matchFound = false;
if (exactMatch) { // assumes that only a single resource has this ID.
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');
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=>{
Object.keys(store).forEach((theKey) => {
if (theKey.startsWith(urlPath)) {
matches.push(store[theKey]);
matchFound = true;
@@ -91,14 +93,14 @@ let serveRestData = function(urlPath, req, resp) {
});
if (matchFound) {
resp.writeHead(200, { 'Content-Type' : 'application/json' });
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.writeHead(404, { "Content-Type": "application/json" });
resp.write('{"success":false, "error":"Not Found"}');
} else {
console.log(" - Match found.");
@@ -106,17 +108,19 @@ let serveRestData = function(urlPath, req, resp) {
resp.end();
break;
case 'PUT':
json = '';
req.on('data', function(chunk) { json += chunk; });
req.on('end', function() {
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.writeHead(201, { "Content-Type": "application/json" });
resp.write('{"success":true}');
} catch (e) {
resp.writeHead(400, { 'Content-Type' : 'application/json' });
resp.writeHead(400, { "Content-Type": "application/json" });
resp.write('{"success":false, "error":"Invalid JSON"}');
}
resp.end();
@@ -124,15 +128,16 @@ let serveRestData = function(urlPath, req, resp) {
});
break;
case 'POST':
json = '';
req.on('data', function(chunk) { json += chunk; });
req.on('end', function() {
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.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,
@@ -144,11 +149,11 @@ let serveRestData = function(urlPath, req, resp) {
store[urlPath] = data;
resp.writeHead(201, { 'Content-Type' : 'application/json' });
resp.writeHead(201, { "Content-Type": "application/json" });
resp.write(JSON.stringify(store[urlPath]));
}
} catch (e) {
resp.writeHead(400, { 'Content-Type' : 'application/json' });
resp.writeHead(400, { "Content-Type": "application/json" });
resp.write('{"success":false, "error":"Invalid JSON"}');
}
resp.end();
@@ -156,20 +161,22 @@ let serveRestData = function(urlPath, req, resp) {
});
break;
case 'PATCH':
json = '';
req.on('data', function(chunk) { json += chunk; });
req.on('end', function() {
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]
Object.keys(data).forEach((theKey) => {
store[urlPath][theKey] = data[theKey];
});
resp.writeHead(201, { 'Content-Type' : 'application/json' });
resp.writeHead(201, { "Content-Type": "application/json" });
resp.write('{"success":true}');
} catch (e) {
resp.writeHead(400, { 'Content-Type' : 'application/json' });
resp.writeHead(400, { "Content-Type": "application/json" });
resp.write('{"success":false, "error":"Invalid JSON"}');
}
resp.end();
@@ -177,18 +184,17 @@ let serveRestData = function(urlPath, req, resp) {
});
break;
case 'OPTIONS':
case "OPTIONS":
resp.end();
break;
case 'DELETE':
case "DELETE":
if (store.hasOwnProperty(urlPath)) {
delete store[urlPath];
resp.writeHead(204, { 'Content-Type' : 'application/json' });
resp.writeHead(204, { "Content-Type": "application/json" });
resp.write('{"success":true}');
} else {
resp.writeHead(404, { 'Content-Type' : 'application/json' });
resp.writeHead(404, { "Content-Type": "application/json" });
resp.write('{"success":false, "error":"Not Found"}');
}
resp.end();
@@ -196,30 +202,32 @@ let serveRestData = function(urlPath, req, resp) {
break;
default:
resp.writeHead(405, { 'Content-Type' : 'application/json' });
resp.writeHead(405, { "Content-Type": "application/json" });
resp.write('{"success":false, "error":"Unsupported method"}');
resp.end();
}
};
//
// Listen for connections
//
http.createServer(function(req, resp) {
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-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');
resp.setHeader("Access-Control-Allow-Methods", "GET, PATCH, POST, PUT, DELETE, OPTIONS");
console.log('Serving [' + req.url + ']');
console.log("Serving [" + req.url + "]");
if (req.url === '/') { // server index file
serveFile('index.html', req, resp);
} else { // rest request
if (req.url === "/") {
// server index file
serveFile("index.html", req, resp);
} else {
// rest request
serveRestData(req.url, req, resp);
}
}).listen(PORT);
})
.listen(PORT);

3
gateways.tf Normal file
View File

@@ -0,0 +1,3 @@
resource "aws_internet_gateway" "test-env-gw" {
vpc_id = "${aws_vpc.test-env.id}"
}

27
main.tf Normal file
View File

@@ -0,0 +1,27 @@
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}"]
tags = {
"Name" = var.ami_name
}
subnet_id = "${aws_subnet.subnet-uno.id}"
}

10
network.tf Normal file
View File

@@ -0,0 +1,10 @@
resource "aws_vpc" "test-env" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
}
resource "aws_eip" "ip-test-env" {
instance = "${aws_instance.app_server.id}"
vpc = true
}

9
outputs.tf Normal file
View 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
}

19
package.json Normal file
View 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"
}

27
security.tf Normal file
View File

@@ -0,0 +1,27 @@
resource "aws_security_group" "ingress-all-test" {
name = "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"]
}
}

17
subnets.tf Normal file
View File

@@ -0,0 +1,17 @@
resource "aws_subnet" "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" {
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}"
}

26
teraformtest1.ppk Normal file
View 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

13
variables.tf Normal file
View 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"
}