---
# System prepended metadata

title: AMZ006453 AWSwebテスト作業ログ

---

# AMZ006453 AWSwebテスト作業ログ
## 課題1
方針：apache+flask+modwsgiでdeployを行う
flaskのアプリケーションは/var/www/aws_testに置く

### リポジトリのアップデート
サーバのターミナルで以下のコマンドを実行した
sudo yum update

### apacheのインストールと自動起動設定
サーバのターミナルで以下のコマンドを実行した
sudo yum install httpd
sudo service httpd start
sudo chkconfig httpd on

### Flaskのアプリケーションを置くディレクトリを作成
flaskのアプリケーションを/var/www/aws_testに配置するため，以下のコマンドを実行した
sudo mkdir /var/www/aws_test

### SFTPの設定
サーバのターミナルのvim上ではなく自分のローカルのVScode上でファイルを編集するためにSFTPを設定した
①VScodeの拡張機能「SFTP」をインストール
②ctrl+shift+p からSFTP:Configを呼び出し，jsonファイルを作成
③jsonファイルを以下のように設定
{
    "name": "My Server",
    "host": "(ipアドレスを記述)",
    "protocol": "sftp",
    "port": 22,
    "username": "ec2-user",
    "privateKeyPath" : "(秘密鍵のパスを設定)",
    "passphrase": true,
    "syncMode": "full",
    "remotePath": "/",
    "uploadOnSave": true,
    "rootPath": "/var/www/aws_test"
}
④ctrl+shift+pからSFTP:List_Allを選択し，編集したサーバ上のフォルダを選択

### Flaskのインストール
サーバのターミナル上で以下のコマンドを実行した
wget https://bootstrap.pypa.io/get-pip.py
sudo python27 get-pip.py
sudo cp /usr/local/bin/pip /usr/sbin/
pip install Flask

### mod_wsgiのインストール
サーバのターミナル上で以下のコマンドを実行した
sudo yum install mod_wsgi-python27

### /var/wwwのパーミッション設定
sudo groupadd www
sudo usermod -a -G www ec2-user
sudo chown -R root:www /var/www
sudo chmod 2775 /var/www
find /var/www -type d -exec sudo chmod 2775 {} \;
find /var/www -type f -exec sudo chmod 0664 {} \;

###  /etc/httpd/conf.dにflask.confの追加
以下のコマンドを実行し，/etc/httpd/conf.dにflask.confを作成および編集を行った．
sudo vim /etc/httpd/conf.d/flask.conf

以下がflask.confの内容である．
WSGISocketPrefix /var/run/wsgi
<VirtualHost *:80>
    ServerName example.com

    WSGIPassAuthorization On
    WSGIDaemonProcess aws_test user=ec2-user group=www threads=5
    WSGIScriptAlias / /var/www/aws_test/deploy.wsgi

    <Directory /var/www/aws_test>
        WSGIProcessGroup aws_test
        WSGIApplicationGroup %{GLOBAL}
        Order deny,allow
        Allow from all
    </Directory>
</VirtualHost>

### /var/www/aws_test/deploy.wsgiの追加
以下のコマンドを実行し，/var/www/aws_testにdeploy.wsgiを作成および編集を行った．
sudo vim /var/www/aws_test/deploy.wsgi

以下がdeploy.wsgiの内容である．
import sys, os
sys.path.append('/var/www/aws_test')
from app import app as application

### /var/www/aws_test/app.pyの編集
/var/www/aws_test/app.pyに以下の内容を追加した
/----------------------------/
from flask import Flask

app = Flask(__name__)

@app.route("/")
def amazon():
    return "AMAZON\n"
    
if __name__ == '__main__':
    app.run()
/----------------------------/

## 課題2
方針:flask_httpauthを使ってベーシック認証を行う

### flask-httpauthのインストール
サーバのターミナル上で以下のコマンドを実行した
pip install flask-httpauth

### /var/www/aws_test/app.pyの編集
/var/www/aws_test/app.pyに以下の内容を追加した
ただし，HTTPBasicAuth→HTTPDigestAuthとすることでDigest認証も実装できる
/----------------------------/
from flask_httpauth import HTTPBasicAuth

#Basic認証の宣言
auth = HTTPBasicAuth()
#Basic認証で利用するユーザの宣言
users = {
    "amazon": "candidate",
}

#ユーザ名とパスワードを照合する関数
@auth.get_password
def get_pw(username):
    if username in users:
        return users.get(username)
    return None

#/secret/で呼び出される関数
@app.route("/secret/")
@auth.login_required
def secret():
    return "SUCCESS\n"
/----------------------------/ 
 
## 課題3
方針:flaskのrequestを使用して引数を取得する．pythonの正規表現モジュールreを用いて，引数が数式かを判別する

### /var/www/aws_test/app.pyの編集
/var/www/aws_test/app.pyに以下の内容を追加した
/----------------------------/
from flask import Flask,request 
import re

#/calcで呼び出される関数
@app.route("/calc")
def calc_strings():
    #/calc?以降の引数を取得
    strings = request.query_string
    #文字列のカッコの数が合わない場合，数式に関わるもの以外が含まれる場合はERRORを出力
    if strings.count("(") != strings.count(")") or re.search("[^\+\-\*\/()0-9]",strings):
        return "ERROR"
    else:
        #文字列の算術計算を行う
        try:
            ans = eval(strings)
        #文字列が計算を行えないものだった場合(例: ++,1+)
        except SyntaxError:
            return "ERROR"
        #文字列が()だった場合
        if type(ans) is tuple:
            return "ERROR"
        return '%d'%ans
/----------------------------/

## 課題4
方針:Flask-SQLAlchemyを利用して，データベースを作成．データベースを用いて在庫管理を行う.
ただしデータベースは以下に配置するものとした.
var/www/aws_test/Stock.db

### Flask-SQLAlchemyのインストール
ターミナル上で以下のコマンドを実行した
sudo pip install Flask-SQLAlchemy

### /var/www/aws_test/app.pyの編集
/var/www/aws_test/app.pyに以下の内容を追加した
/----------------------------/
from flask_sqlalchemy import SQLAlchemy

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////var/www/aws_test/Stock.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

#データベースで利用するモデルの宣言(課題4)
class Stock(db.Model):
    __tablename__ = 'Stock&Sales'
    id = db.Column(db.Integer, primary_key = True)
    name = db.Column(db.String(8), unique = True)
    amount = db.Column(db.Integer)
    sales = db.Column(db.Float)
    
    def __init__(self, name, amount, sales):
        self.name = name
        self.amount = amount
        self.sales = sales
        
    def __repr__(self):
        return '<Name %r>' % self.name

#/stockerで呼び出される関数 
@app.route("/stocker")
def get_function():
    #必要な引数の宣言
    function = ""
    name = None
    amount = None
    price = None
    #/stocker?以降の文字列を取得
    strings = request.query_string
    
    #引数が含まれていたら取得する
    if "function=" in strings:
        function = request.args.get("function",type=str)
    if "name=" in strings:
        name = request.args.get("name",type=str)
    if "amount=" in strings:
        amount = request.args.get("amount",type=str)
    if "price=" in strings:
        price = request.args.get("price",type=str)
    
    #amountのエラーチェック
    if amount is None:
        amount = "1"
    #数字以外の要素(記号や英字)が含まれているか判別
    elif amount.isdigit() is False:
        return "ERROR"
    
    #priceのエラーチェック
    if price is None:
        pass
    else:
        #数字として扱える文字列か判別(例: 1.1.1,abeなど)
        try: 
            float(price)
        except ValueError:
            return "ERROR"
        #小数点を除いた文字列が数字のみかを判別
        if price.replace(".","").isdigit() is False:
            return "ERROR"
            
    #functionに対応した関数を呼び出す        
    if function == "addstock":
        if name is None:
            return "ERROR"
        else:
            addstock(name,amount)   
            return ""         
    
    elif function == "checkstock":
        product_string = checkstock(name)
        return product_string
    
    elif function == "sell":
        if name is None:
            return "ERROR"
        else:
            product_string = sell(name,amount,price)
            return product_string
    
    elif function == "checksales":
        product_string = checksales()
        return product_string

    elif function == "deleteall":
        deleteall()
        return ""
    else:
        return "ERROR"

#function=addstockで呼び出される関数
def addstock(name,amount):
    amount = int(amount)
    #テーブルからnameの列を呼び出す
    stock = Stock.query.filter_by(name=name).first()
    if stock is None:
        stock = Stock(name,amount,0)
    else:
        stock.amount += amount
    #テーブルにnameの列を追加or上書き
    db.session.add(stock)
    #テーブルの保存
    db.session.commit()

#function=checkstockで呼び出される関数
def checkstock(name):
    product_string = ""
    if name is None:
        #テーブルの全ての列をnameで昇順にソートして呼び出す
        products = Stock.query.order_by(Stock.name.asc()).all()
        for product in products:
            product_string += (product.name + ": " + str(product.amount) + "\n") 
        return product_string
    else:
        product = Stock.query.filter_by(name=name).first()
        if product is None:
            return "ERROR"
        else:
            return product.name + ": " + str(product.amount)

#function=sellで呼び出される関数
def sell(name,amount,price):
    amount = int(amount)
    stock = Stock.query.filter_by(name=name).first()
    if stock is None:
        return "ERROR"
    #在庫が足りなかったらERRORを出力
    elif stock.amount < amount:
        return "ERROR"
    else:
        stock.amount = stock.amount - amount
        #価格が指定されたら売り上げを計算
        if price is not None:
            price = float(price)
            stock.sales = stock.sales + amount * price
        db.session.add(stock)
        db.session.commit()
        return ""

#function=checksalesで呼び出される関数
def checksales():
    #テーブルの全ての列を呼び出す
    products = Stock.query.all()
    total_sales = 0
    for product in products:
        total_sales += product.sales
    #売り上げが小数か整数か判別
    if total_sales == int(total_sales):
        total_sales = int(total_sales)
    else:
        total_sales = round(total_sales,2)
    return "sales: " + str(total_sales) 

#function=deleteallで呼び出される関数
def deleteall():
    #全てのテーブルの列を削除
    Stock.query.delete()
    db.session.commit()
/----------------------------/

### データベースの作成
app.pyの編集後にターミナルで以下のコマンドを実行し，データベースを作成した
cd /var/www/aws_test
ipython
[1] from app import db
[2] db.create_all()


## AWS面接
流れ
〇自己紹介(5分くらい？)
ESに書いたことを簡単に聞かれたりはした(興味のある技術のこと)

〇面接官からの質問(技術的なことをかなり細かく聞かれた)
時間配分は技術テスト:提出課題で3:7くらいの割合だったと思う
まず,技術テストのことについて聞かれた．例：課題3でeval関数を使っているけど，これを使うことのデメリットはありますか？今回作ったプログラムを複数のサーバーで同時に運用しているときに，プログラムをアップデートしたいときにサーバーを止めずにアップデートする方法はありますか？など
次に提出した課題についてかなり細かく聞かれた．例：この部分でサーバーにエラーが発生した際はどこで検知できるか？この部分を付けているがそのメリットは何か？この機能はどういうものか？
正直課題で使った機能とそれが及ぶ範囲は全部把握しといてもいいんじゃないかって思えるレベルだった．
〇逆質問




