Skip to content

Commit c8f4660

Browse files
authored
Merge pull request #7 from f5devcentral/dev
Dev
2 parents 87681cc + 5b4f947 commit c8f4660

26 files changed

+519
-185
lines changed

cloudapp/app/templates/pretty_echo.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@
3333
<div class="container">
3434
<div>
3535
<h1 class="mt-5">
36-
<img src="https://github.com/f5devcentral/f5xc-lab-mcn-practical/blob/main/cloudapp/static/logo.png?raw=true" alt="" width="auto" height="100"/>
36+
<img src="https://raw.githubusercontent.com/f5devcentral/f5xc-lab-mcn-practical/main/cloudapp/app/static/logo.png" alt="" width="auto" height="100"/>
3737
MCN Practical Cloud App</h1>
3838
</div>
3939
<div class="card mt-3">
4040
<div class="card-header">Environment</div>
4141
<div class="card-body">
4242
{% if request_env == 'AWS' %}
43-
<img src="https://github.com/f5devcentral/f5xc-lab-mcn-practical/blob/main/cloudapp/app/static/aws.png" alt="" width="auto" height="40">
43+
<img src="https://raw.githubusercontent.com/f5devcentral/f5xc-lab-mcn-practical/main/cloudapp/app/static/aws.png" alt="" width="auto" height="40">
4444
{% elif request_env == 'Azure' %}
45-
<img src="https://github.com/f5devcentral/f5xc-lab-mcn-practical/blob/main/cloudapp/app/static/azure.png" alt="" width="auto" height="40">
45+
<img src="https://raw.githubusercontent.com/f5devcentral/f5xc-lab-mcn-practical/main/cloudapp/app/static/azure.png" alt="" width="auto" height="40">
4646
{% else %}
47-
<img src="https://github.com/f5devcentral/f5xc-lab-mcn-practical/blob/main/cloudapp/app/static/flask.png" alt="" width="auto" height="40">
47+
<img src="https://raw.githubusercontent.com/f5devcentral/f5xc-lab-mcn-practical/main/cloudapp/app/static/flask.png" alt="" width="auto" height="40">
4848
{% endif %}
4949
&nbsp{{ request_env }}
5050
</div>

labapp/app/app.py

+73-56
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""
22
Flask app for lab/guide
33
"""
4+
import os
5+
import re
46
from flask import Flask, render_template, jsonify, request, redirect, make_response, flash, url_for
57
from flask_caching import Cache
68
import requests
79
import markdown
8-
import validators
9-
import os
1010
from ce import get_ce_info, get_ce_state
1111

1212
app = Flask(__name__)
@@ -15,11 +15,31 @@
1515
if app.config['udf']:
1616
info = get_ce_info()
1717
app.config['ce_info'] = info
18-
app.config['base_url'] = "lab-mcn.f5demos.com"
18+
app.config['base_url'] = "mcn-lab.f5demos.com"
1919
app.config['CACHE_TYPE'] = 'SimpleCache'
2020
cache = Cache(app)
2121
app.secret_key = "blahblahblah"
2222

23+
def render_md(file: str) -> str:
24+
"""render markdown w/ common extentions"""
25+
with open(file, "r") as file:
26+
content = file.read()
27+
html = markdown.markdown(
28+
content,
29+
extensions=['markdown.extensions.attr_list','markdown.extensions.codehilite','markdown.extensions.fenced_code']
30+
)
31+
return html
32+
33+
def validate_eph_ns(input_name):
34+
"""validate ephemeral namespace name"""
35+
pattern = r'^[a-zA-Z]+-[a-zA-Z]+$'
36+
return bool(re.match(pattern, input_name))
37+
38+
def eph_ns() -> str:
39+
"""check if ephemeral namespace is set"""
40+
eph_ns = request.cookies.get('eph_ns', None)
41+
return eph_ns
42+
2343
@app.errorhandler(404)
2444
@app.errorhandler(500)
2545
def return_err(err):
@@ -32,92 +52,89 @@ def return_err(err):
3252

3353
@app.route('/')
3454
def index():
35-
with open("markdown/overview.md", "r") as file:
36-
content = file.read()
37-
html = markdown.markdown(content)
55+
"""index page"""
56+
html = render_md("markdown/overview.md")
3857
return render_template('overview.html', content=html)
3958

4059
@app.route('/setup', methods=['GET', 'POST'])
4160
def setup():
61+
"""setup page"""
4262
if request.method == 'POST':
4363
action = request.form['action']
4464
if action == 'save':
45-
base_url = request.form['base_url'].strip()
46-
if not validators.domain(base_url):
47-
flash("Invalid domain format.", "info")
48-
return redirect(url_for('setup'))
49-
if not base_url.endswith(app.config['base_url']):
50-
flash(f"Domain must end with {app.config['base_url']}.", "info")
65+
eph_ns = request.form['eph_ns'].strip()
66+
print(eph_ns)
67+
if not validate_eph_ns(eph_ns):
68+
flash("Invalid ephemeral NS.", "danger")
5169
return redirect(url_for('setup'))
5270
response = make_response(redirect('/setup'))
53-
response.set_cookie('base_url', base_url, max_age=60*60*24)
54-
flash("Domain successfully set.", "success")
71+
response.set_cookie('eph_ns', eph_ns, max_age=60*60*24)
72+
flash("Ephemeral NS successfully set.", "success")
5573
return response
5674
elif action == 'clear':
5775
response = make_response(redirect('/setup'))
58-
response.set_cookie('base_url', '', expires=0)
59-
flash("Domain setting cleared.", "info")
76+
response.set_cookie('eph_ns', '', expires=0)
77+
flash("Ephemeral NS cleared.", "info")
6078
return response
61-
return render_template('setup.html', base_url=app.config['base_url'])
79+
html = render_md("markdown/setup.md")
80+
return render_template('setup.html', content=html)
81+
82+
@app.route('/arch')
83+
def arch():
84+
"""arch page"""
85+
html = render_md("markdown/arch.md")
86+
return render_template('standard.html', content=html, title="MCN Practical: Architecture")
6287

6388
@app.route('/_ce_state')
6489
@cache.cached(timeout=30)
6590
def ce_state():
91+
"""get ce state (internal route)"""
6692
data = get_ce_state(app.config['ce_info'])
6793
return data
6894

69-
@app.route('/test')
70-
def test():
71-
base_url = request.cookies.get('base_url')
72-
url = f"https://echo.{base_url}"
73-
try:
74-
response = requests.get(url)
75-
response.raise_for_status()
76-
return jsonify(status='success', data=response.json())
77-
except requests.RequestException as e:
78-
return jsonify(status='fail', error=str(e))
79-
8095
@app.route('/lb')
8196
def lb():
82-
with open("markdown/lb.md", "r") as file:
83-
content = file.read()
84-
html = markdown.markdown(
85-
content,
86-
extensions=['markdown.extensions.codehilite','markdown.extensions.fenced_code']
87-
)
88-
return render_template('lb.html', content=html)
97+
"""lb page"""
98+
ns = eph_ns()
99+
html = render_md("markdown/lb.md")
100+
return render_template('exercise_standard.html', title="MCN Practical: LB", content=html, ns=ns)
89101

90102
@app.route('/path')
91103
def path():
92-
with open("markdown/path.md", "r") as file:
93-
content = file.read()
94-
html = markdown.markdown(
95-
content,
96-
extensions=['markdown.extensions.codehilite','markdown.extensions.fenced_code']
97-
)
98-
return render_template('path.html', content=html)
104+
"""path page"""
105+
ns = eph_ns()
106+
html = render_md("markdown/path.md")
107+
return render_template('exercise_standard.html', title="MCN Practical: Path Routing", content=html, ns=ns)
99108

100109
@app.route('/header')
101110
def header():
102-
with open("markdown/header.md", "r") as file:
103-
content = file.read()
104-
html = markdown.markdown(
105-
content,
106-
extensions=['markdown.extensions.codehilite','markdown.extensions.fenced_code']
107-
)
108-
return render_template('header.html', context=html)
111+
"""header page"""
112+
ns = eph_ns()
113+
html = render_md("markdown/header.md")
114+
return render_template('exercise_standard.html', title="MCN Practical: Headers", content=html, ns=ns)
109115

110-
@app.route('/appCon-aws')
111-
def make_request_ac1_aws():
116+
@app.route('/_lb_aws')
117+
def lb_aws():
118+
"""AWS LB test"""
112119
try:
113-
response = requests.get('https://ifconfig.io/all.json')
114-
response.raise_for_status()
120+
ns = eph_ns()
121+
if not ns:
122+
raise Exception("Ephemeral NS not set.")
123+
url = f"https://{ns}.{app.config['base_url']}/raw"
124+
print(url)
125+
response = requests.get(url, timeout=5)
126+
print(response.text)
127+
print(response.json())
128+
response.raise_for_status()
129+
if response.json()['request_env'] != "AWS":
130+
raise Exception("Invalid request env.")
115131
return jsonify(status='success', data=response.json())
116-
except requests.RequestException as e:
132+
except Exception as e:
117133
return jsonify(status='fail', error=str(e))
118134

119-
@app.route('/appCon-azure')
120-
def make_request_ac1_azure():
135+
@app.route('/_lb_azure')
136+
def lb_azure():
137+
"""Azure LB test"""
121138
try:
122139
response = requests.get('https://ifconfig1.io/all.json')
123140
response.raise_for_status()

labapp/app/markdown/arch.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none">
2+
<img src="/static/arch.png" width="300px" height="auto" alt="arch">
3+
</div>
4+
5+
# **Architecture**
6+
7+
<div href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none border-bottom"></div>
8+
9+
10+
<div class="err">
11+
<a href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none">
12+
<img src="/static/coming-soon.png" alt="Descriptive Text">
13+
</a>
14+
</div>

labapp/app/markdown/header.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<div href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none">
2+
<img src="/static/path.png" width="300px" height="auto" alt="intro">
3+
</div>
4+
5+
# **Header Manipulation**
6+
7+
<div href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none border-bottom"></div>
8+
9+
<div style="height:25px"></div>
10+
11+
### **Exercise 1: Add/Remove**
12+
13+
HERE
14+
15+
<div class="left-aligned-button-container">
16+
<button id="requestBtn2" class="btn btn-primary">Test Load Balancer</button>
17+
</div>
18+
<div id="result2" class="mt-3"></div>
19+
<script>
20+
document.getElementById('requestBtn2').addEventListener('click', async () => {
21+
const resultDiv = document.getElementById('result2');
22+
try {
23+
const response = await axios.get('/_lb_azure');
24+
if(response.data.status === 'success') {
25+
const prettyJson = JSON.stringify(response.data.data, null, 4);
26+
resultDiv.innerHTML = `<pre class="alert alert-success"><code>${prettyJson}</code></pre>`;
27+
} else {
28+
resultDiv.innerHTML = `<div class="alert alert-danger"><b>Request Failed</b></div>`;
29+
}
30+
resultDiv.scrollIntoView({ behavior: 'smooth', block: 'end' }); // Smooth scroll to the resultDiv
31+
} catch (error) {
32+
resultDiv.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
33+
resultDiv.scrollIntoView({ behavior: 'smooth', block: 'end' }); // Smooth scroll to the resultDiv
34+
}
35+
});
36+
</script>
37+
38+
<div style="height:25px"></div>
39+
40+
Nice 🚀! If you've completed all the exercises so far, you have a good foundation for how App Connect addresses common L7 MCN scenarios.
41+
In subsequent labs, we'll explore security and observabilty concepts that build on MCN functionality.
42+
Head over to the <a href="/vnet" class="alert-link">Network Connect</a> exercise.
43+
44+
<div style="height:25px"></div>

labapp/app/markdown/home-overview.md

-5
This file was deleted.

0 commit comments

Comments
 (0)