diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..de19707 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +lib +*.dwarf +.shards +.git diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06dbc94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +lib +.shards +*.dwarf + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c7703e0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM crystallang/crystal:latest +WORKDIR /src/ +COPY . . +RUN shards install +RUN crystal build --release --link-flags="-static" src/server.cr + +FROM alpine:latest +COPY --from=0 /src/server /server +COPY --from=0 /src/code_hash.txt /code_hash.txt +EXPOSE 3000 +CMD ["/server"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae73581 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Amazon ECS Workshop + +This is part of an Amazon ECS workshop at https://ecsworkshop.com diff --git a/buildspec-test.yml b/buildspec-test.yml new file mode 100644 index 0000000..2e690f2 --- /dev/null +++ b/buildspec-test.yml @@ -0,0 +1,13 @@ +version: 0.2 + +phases: + install: + commands: + - export IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) + - export ACCOUNT=$(echo $CODEBUILD_BUILD_ARN |cut -f5 -d:) + - export $(cat mu-env.sh) + - echo '***** This is the current env:' + - printenv + build: + commands: + - curl -m3 -sL -w "%{http_code}\\n" "$BASE_URL" -o /dev/null diff --git a/buildspec.yml b/buildspec.yml new file mode 100644 index 0000000..abda502 --- /dev/null +++ b/buildspec.yml @@ -0,0 +1,18 @@ +version: 0.2 + +phases: + install: + commands: + - export IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) + - export ACCOUNT=$(echo $CODEBUILD_BUILD_ARN |cut -f5 -d:) + - echo '***** This is the current env:' + - printenv + build: + commands: + post_build: + commands: + - printf '[{"name":"ecsdemo-crystal","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json + - echo $IMAGE_TAG > code_hash.txt +artifacts: + files: + - '**/*' diff --git a/code_hash.txt b/code_hash.txt new file mode 100644 index 0000000..a7c041f --- /dev/null +++ b/code_hash.txt @@ -0,0 +1 @@ +NOHASH diff --git a/mu.yml b/mu.yml new file mode 100644 index 0000000..39bf13d --- /dev/null +++ b/mu.yml @@ -0,0 +1,16 @@ +--- +service: + desiredCount: 3 + maxSize: 6 + port: 3000 + priority: 2 + pathPatterns: + - /crystal + networkMode: awsvpc +parameters: + 'mu-service-ecsdemo-crystal-acceptance': + ElbHttpListenerArn: + mu-loadbalancer-acceptance-BackendLBHttpListenerArn + 'mu-service-ecsdemo-crystal-production': + ElbHttpListenerArn: + mu-loadbalancer-production-BackendLBHttpListenerArn diff --git a/shard.lock b/shard.lock new file mode 100644 index 0000000..2312b61 --- /dev/null +++ b/shard.lock @@ -0,0 +1,6 @@ +version: 1.0 +shards: + interface_address: + github: t-richards/interface_address + version: 1.0.0 + diff --git a/shard.yml b/shard.yml new file mode 100644 index 0000000..d524a7f --- /dev/null +++ b/shard.yml @@ -0,0 +1,7 @@ +name: shards +version: 0.1.0 + +dependencies: + interface_address: + github: t-richards/interface_address + version: ~> 1.0.0 diff --git a/src/default_ip/default_ip.cr b/src/default_ip/default_ip.cr new file mode 100644 index 0000000..eedf954 --- /dev/null +++ b/src/default_ip/default_ip.cr @@ -0,0 +1,33 @@ +require "interface_address" + +class DefaultIf + @@default_if : String = "NONE" + @@default_ip : String = "NONE" + + def self.getif + interface = IO::Memory.new + command = "awk '$2 == 00000000 { print $1 }' /proc/net/route" + getroute = Process.run(command, shell: true, output: interface) + if !getroute.success? || !getroute.normal_exit? || getroute.signal_exit? + command = "route -n get default | awk '/interface/ {print $2}'" + getroute = Process.run(command, shell: true, output: interface) + end + interface = interface.to_s + interface = interface.gsub "\n","" + @@default_if = interface + end + + def self.getip + @@default_if = DefaultIf.getif + InterfaceAddress.get_interface_addresses.each do |a| + iface_name = a.interface_name + iface_ip = a.ip_address.address + if @@default_if == iface_name + if iface_ip.includes? "." + @@default_ip = iface_ip + return @@default_ip + end + end + end + end +end diff --git a/src/server.cr b/src/server.cr new file mode 100644 index 0000000..9bd81e4 --- /dev/null +++ b/src/server.cr @@ -0,0 +1,55 @@ +require "logger" +require "http/server" +require "./default_ip" + +log = Logger.new(STDOUT) +log.level = Logger::DEBUG + +log.debug("Created logger") +log.info("Program started") +log.warn("Nothing to do!") + +code_hash = File.read("code_hash.txt") +log.debug("code_hash is: #{code_hash}") + +default_ip = DefaultIf.getip +log.debug("default_ip is: #{default_ip}") + +if default_ip + net_octet = default_ip.split(".")[2] + log.debug("net_octet is: #{net_octet}") +end + +case net_octet + when "100" + az = "1a" + when "101" + az = "1b" + when "102" + az = "1c" +else + az = "unknown" +end +log.debug("az is: #{az}") + +server = HTTP::Server.new("0.0.0.0", 3000, + [ + HTTP::ErrorHandler.new, + HTTP::LogHandler.new, + HTTP::CompressHandler.new, + ]) do |context| + if context.request.path == "/crystal" || context.request.path == "/crystal/" + context.response.content_type = "text/plain" + context.response.print "Crystal backend: Hello! from #{default_ip} in AZ-#{az} commit #{code_hash}" + elsif context.request.path == "/health" + context.response.content_type = "text/plain" + context.response.print "Healthy!" + else + context.response.status_code = 404 + context.response.headers["Content-Type"] = "text/plain" + context.response.puts "Not Found" + end + end + +puts "Listening on http://0.0.0.0:3000" +server.listen