Skip to content

Commit

Permalink
Merge pull request #11 from f5devcentral/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
kreynoldsf5 authored Apr 30, 2024
2 parents c8f4660 + 96f3e1a commit e382820
Show file tree
Hide file tree
Showing 25 changed files with 638 additions and 282 deletions.
14 changes: 14 additions & 0 deletions cloudapp/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ def create_app():
def to_pretty_json(value):
return json.dumps(value, sort_keys=True, indent=4)

@app.errorhandler(401)
@app.errorhandler(404)
@app.errorhandler(500)
def return_err(err):
return {
'error': err.description
}

@app.route('/raw', methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
def echo():
"""
Expand All @@ -33,6 +41,12 @@ def echo():
response['request_data'] = data
return jsonify(response)

@app.route('/<env>/raw', methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
def env_echo(env):
if env.lower() == app.config['site'].lower():
return echo()
return jsonify({'error': 'Invalid environment'})

@app.route('/', methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
@app.route('/echo', methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
def echo_html():
Expand Down
22 changes: 21 additions & 1 deletion cloudapp/app/zappa_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,30 @@
"profile_name": "default",
"project_name": "cloudapp",
"runtime": "python3.11",
"s3_bucket": "mcn-prac-cloudapp",
"s3_bucket": "mcn-prac-cloudapp-dev",
"aws_region": "us-east-1",
"environment_variables": {
"SITE": "AWS"
}
},
"prod": {
"app_function": "app.app",
"exclude": [
"boto3",
"dateutil",
"botocore",
"s3transfer",
"concurrent"
],
"profile_name": "default",
"project_name": "cloudapp",
"runtime": "python3.11",
"s3_bucket": "mcn-prac-cloudapp-prod",
"aws_region": "us-east-1",
"environment_variables": {
"SITE": "AWS"
},
"certificate_arn": "arn:aws:acm:us-east-1:317124676658:certificate/6149d976-8908-46f9-aa4a-e518b1dc16aa",
"domain": "aws-mcn-practical.f5demos.com"
}
}
10 changes: 4 additions & 6 deletions labapp/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
FROM python:3.10.14-slim
#FROM python:3.10.14-slim
FROM python:3.11-alpine3.18
LABEL org.opencontainers.image.description MCN Practical Lab App

ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY app .
RUN pip install --no-cache-dir -r requirements.txt

EXPOSE 5000
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0

CMD ["flask", "run"]
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "app:app"]
1 change: 1 addition & 0 deletions labapp/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

178 changes: 135 additions & 43 deletions labapp/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@
from ce import get_ce_info, get_ce_state

app = Flask(__name__)
app.config['udf'] = os.getenv('UDF', False)
info = None
if app.config['udf']:
info = get_ce_info()
app.config['ce_info'] = info
app.config['ce_info'] = None
app.config['UDF'] = None
if os.getenv('UDF', None):
app.config['ce_info'] = get_ce_info()
app.config['UDF'] = True
app.config['base_url'] = "mcn-lab.f5demos.com"
app.config['CACHE_TYPE'] = 'SimpleCache'
cache = Cache(app)
app.secret_key = "blahblahblah"

class LabException(Exception):
"""lab exception"""

def render_md(file: str) -> str:
"""render markdown w/ common extentions"""
with open(file, "r") as file:
content = file.read()
with open(file, "r", encoding="utf-8") as md:
content = md.read()
html = markdown.markdown(
content,
extensions=['markdown.extensions.attr_list','markdown.extensions.codehilite','markdown.extensions.fenced_code']
extensions=['markdown.extensions.attr_list','markdown.extensions.fenced_code']
)
return html

Expand All @@ -37,8 +40,33 @@ def validate_eph_ns(input_name):

def eph_ns() -> str:
"""check if ephemeral namespace is set"""
eph_ns = request.cookies.get('eph_ns', None)
return eph_ns
this_eph_ns = request.cookies.get('eph_ns', None)
return this_eph_ns

def cloudapp_fetch(url, timeout, prop, value, headers = {}):
"""
Fetch data from URL
Validate prop and value in the JSON response
"""
response = requests.get(url, timeout=timeout, headers=headers)
response.raise_for_status()
data = response.json()
if data.get(prop) != value:
raise ValueError(f"Invalid {prop}: expected {value}, got {data.get(prop)}")
clean_headers = headers_cleaner(data['request_headers'])
data['request_headers'] = clean_headers
return data

def headers_cleaner(headers):
"""
Remove headers that contain specific substrings.
"""
unwanted_substrings = ['x-envoy', 'cloudfront', 'x-k8se']
filtered_headers = {
key: value for key, value in headers.items()
if not any(substring in key.lower() for substring in unwanted_substrings)
}
return filtered_headers

@app.errorhandler(404)
@app.errorhandler(500)
Expand All @@ -54,38 +82,49 @@ def return_err(err):
def index():
"""index page"""
html = render_md("markdown/overview.md")
return render_template('overview.html', content=html)
return render_template('standard.html',
title="MCN Practical: Overview",
content=html,
udf=app.config['UDF']
)

@app.route('/setup', methods=['GET', 'POST'])
def setup():
"""setup page"""
if request.method == 'POST':
action = request.form['action']
if action == 'save':
eph_ns = request.form['eph_ns'].strip()
print(eph_ns)
if not validate_eph_ns(eph_ns):
this_eph_ns = request.form['eph_ns'].strip()
if not validate_eph_ns(this_eph_ns):
flash("Invalid ephemeral NS.", "danger")
return redirect(url_for('setup'))
response = make_response(redirect('/setup'))
response.set_cookie('eph_ns', eph_ns, max_age=60*60*24)
response.set_cookie('eph_ns', this_eph_ns, max_age=60*60*24)
flash("Ephemeral NS successfully set.", "success")
return response
elif action == 'clear':
if action == 'clear':
response = make_response(redirect('/setup'))
response.set_cookie('eph_ns', '', expires=0)
flash("Ephemeral NS cleared.", "info")
return response
html = render_md("markdown/setup.md")
return render_template('setup.html', content=html)
return render_template('setup.html',
title="MCN Practical: Setup",
content=html,
udf=app.config['UDF']
)

@app.route('/arch')
def arch():
"""arch page"""
html = render_md("markdown/arch.md")
return render_template('standard.html', content=html, title="MCN Practical: Architecture")
return render_template('standard.html',
title="MCN Practical: Architecture",
content=html,
udf=app.config['UDF']
)

@app.route('/_ce_state')
@app.route('/_ce_status')
@cache.cached(timeout=30)
def ce_state():
"""get ce state (internal route)"""
Expand All @@ -97,50 +136,103 @@ def lb():
"""lb page"""
ns = eph_ns()
html = render_md("markdown/lb.md")
return render_template('exercise_standard.html', title="MCN Practical: LB", content=html, ns=ns)
return render_template('exercise_standard.html',
title="MCN Practical: LB",
content=html,
ns=ns,
udf=app.config['UDF']
)

@app.route('/path')
@app.route('/route')
def path():
"""path page"""
"""routing page"""
ns = eph_ns()
html = render_md("markdown/path.md")
return render_template('exercise_standard.html', title="MCN Practical: Path Routing", content=html, ns=ns)
html = render_md("markdown/route.md")
return render_template('exercise_standard.html',
title="MCN Practical: HTTP Routing",
content=html,
ns=ns,
udf=app.config['UDF']
)

@app.route('/header')
def header():
"""header page"""
ns = eph_ns()
html = render_md("markdown/header.md")
return render_template('exercise_standard.html', title="MCN Practical: Headers", content=html, ns=ns)
return render_template('exercise_standard.html',
title="MCN Practical: Headers",
content=html,
ns=ns,
udf=app.config['UDF']
)

@app.route('/_lb_aws')
@app.route('/_lb1')
def lb_aws():
"""AWS LB test"""
"""Azure LB test"""
try:
ns = eph_ns()
if not ns:
raise Exception("Ephemeral NS not set.")
raise LabException("Ephemeral NS not set")
url = f"https://{ns}.{app.config['base_url']}/raw"
print(url)
response = requests.get(url, timeout=5)
print(response.text)
print(response.json())
response.raise_for_status()
if response.json()['request_env'] != "AWS":
raise Exception("Invalid request env.")
return jsonify(status='success', data=response.json())
except Exception as e:
data = cloudapp_fetch(url, 5, 'request_env', 'AWS')
return jsonify(status='success', data=data)
except (LabException, requests.RequestException, ValueError) as e:
return jsonify(status='fail', error=str(e))

@app.route('/_lb_azure')
@app.route('/_lb2')
def lb_azure():
"""Azure LB test"""
try:
response = requests.get('https://ifconfig1.io/all.json')
response.raise_for_status()
return jsonify(status='success', data=response.json())
except requests.RequestException as e:
ns = eph_ns()
if not ns:
raise LabException("Ephemeral NS not set")
url = f"https://{ns}.{app.config['base_url']}/raw"
data = cloudapp_fetch(url, 5, 'request_env', 'Azure')
return jsonify(status='success', data=data)
except (LabException, requests.RequestException, ValueError) as e:
return jsonify(status='fail', error=str(e))

@app.route('/_route1')
def route1():
"""First Route Test"""
try:
ns = eph_ns()
if not ns:
raise LabException("Ephemeral NS not set")
base_url = app.config['base_url']
aws_url = f"https://{ns}.{base_url}/aws/raw"
azure_url = f"https://{ns}.{base_url}/azure/raw"
aws_data = cloudapp_fetch(aws_url, 5, 'request_env', 'AWS')
azure_data = cloudapp_fetch(azure_url, 5, 'request_env', 'Azure')
data = {
"aws": aws_data,
"azure": azure_data
}
return jsonify(status='success', data=data)
except (LabException, requests.RequestException, ValueError) as e:
return jsonify(status='fail', error=str(e))

@app.route('/_route2')
def route2():
"""First Route Test"""
try:
ns = eph_ns()
if not ns:
raise LabException("Ephemeral NS not set")
base_url = app.config['base_url']
aws_url = f"https://{ns}.{base_url}/aws/raw"
azure_url = f"https://{ns}.{base_url}/azure/raw"
aws_data = cloudapp_fetch(aws_url, 5, 'request_env', 'AWS', headers={"X-MCN-lab": "aws"})
azure_data = cloudapp_fetch(azure_url, 5, 'request_env', 'Azure', headers={"X-MCN-lab": "azure"})
data = {
"aws": aws_data,
"azure": azure_data
}
return jsonify(status='success', data=data)
except (LabException, requests.RequestException, ValueError) as e:
return jsonify(status='fail', error=str(e))


if __name__ == '__main__':
app.run(debug=False)
app.run(host='0.0.0.0', port=5001, debug=False)
3 changes: 2 additions & 1 deletion labapp/app/ce.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ def get_ce_state(ce_info: dict) -> dict:
ce_state = response.json()['state']
return {
"err": False,
"state": ce_state
"state": ce_state,
"site_name": ce_info['site_name']
}
else:
raise Exception(e)
Expand Down
4 changes: 2 additions & 2 deletions labapp/app/markdown/header.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none">
<img src="/static/path.png" width="300px" height="auto" alt="intro">
<img src="/static/header.png" width="300px" height="auto" alt="intro">
</div>

# **Header Manipulation**
Expand All @@ -20,7 +20,7 @@ HERE
document.getElementById('requestBtn2').addEventListener('click', async () => {
const resultDiv = document.getElementById('result2');
try {
const response = await axios.get('/_lb_azure');
const response = await axios.get('/_head1');
if(response.data.status === 'success') {
const prettyJson = JSON.stringify(response.data.data, null, 4);
resultDiv.innerHTML = `<pre class="alert alert-success"><code>${prettyJson}</code></pre>`;
Expand Down
Loading

0 comments on commit e382820

Please sign in to comment.