Skip to content

add webapp from source sample #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/webapp-source/.env.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
IMAGE_NAME=go-sample
IMAGE_TAG=latest

AZURE_BASE_NAME=go-webapp-tester
AZURE_DEFAULT_LOCATION=westus2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove l7-8 from the template.

# required for continous container build setup
GH_TOKEN=
2 changes: 2 additions & 0 deletions apps/webapp-source/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out/
.env
20 changes: 20 additions & 0 deletions apps/webapp-source/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions apps/webapp-source/Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[prune]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary. This project has no external dependencies and the dep materials should be removed; it's confusing.

go-tests = true
unused-packages = true

[[constraint]]
branch = "master"
name = "golang.org/x/net"
20 changes: 20 additions & 0 deletions apps/webapp-source/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright Microsoft Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
32 changes: 32 additions & 0 deletions apps/webapp-source/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
PACKAGE = github.com/Azure-Samples/azure-sdk-for-go-samples/apps/basic_web_app
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue with using a makefile is that it excludes users who are in PowerShell environments. It's up to you whether or not we want to explicitly provide build support in the samples for pwsh users, or if we want to only focus on Linux/WSL for automated builds, but it's something to consider. This also affects setup.sh.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also did a quick check to see if make is called anywhere - it's not. This file is used for container/binary deploy only, not source, and it can be removed.


REGISTRY = local
IMAGE = go-sample
TAG = latest

PORT = 8080
IMAGE_URI = $(REGISTRY)/$(IMAGE):$(TAG)
C = $(IMAGE)-tester
HOST = localhost:$(PORT)

binary:
mkdir -p out
go build -o out/server .
./out/server &
curl http://$(HOST)/?name=josh && echo ""
pkill --euid $(USER) --newest --exact server

container:
docker build -t $(IMAGE_URI) .
# docker push $(IMAGE_URI)
docker run -d --rm \
--name $C \
--publish "$(PORT):8080" \
$(IMAGE_URI)
curl "http://$(HOST)/?name=josh" && echo ""
docker container logs $C
docker container stop $C
echo "start a new container with"
echo " \`docker run [-d|-it] -p $(PORT):8080 $(IMAGE_URI)\`"

.PHONY: binary container
56 changes: 56 additions & 0 deletions apps/webapp-source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Web App from source

**NOTE**: support for Go web apps from source is in preview and not supported
for production apps yet. The feature is hidden behind a flag which the scripts
here use.

Continuously build and deploy a Go web app using Azure App Service and GitHub,
no container required.

This package includes a basic Go web app and scripts to set up infrastructure
for it in Azure.

Requires [Azure CLI][].

## Try It!

1. Copy the contents of this package to a new repo.
1. Set configuration in a local `.env` file in the package root by copying
`.env.tpl` to `.env`.
1. Change `AZURE_BASE_NAME` to something relatively unique to you; for example
you might include your name.
1. Change `REPO_NAME` to your org and repo in GitHub, e.g. `joshgav/go-sample`.
1. Run ./[setup.sh][] to set up an App Service web app connected to the
specified repo. Don't forget to `git push` your code there too!

**NOTE**: GitHub sync requires a [GitHub personal access
token](https://github.com/settings/tokens); you need to get one from the linked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indicate the level of permissions required for the token in order for the deployment to operate. Assuming it's repo?

page and set it in a local environment variable `GH_TOKEN`, e.g. `export
GH_TOKEN=mylongtokenstring`. You can also add it to your local `.env` file for
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't suggest doing this, it's a security risk since it publishes an auth token to a public resource.

persistence.

To test continuous integration, now make a change and `git push` it to your
repo. The GitHub sync task should detect the change and rebuild and refresh
your web app.

## Details

[setup.sh][] ensures an Azure resource group, app service plan, and
source-based web app are provisioned and connected in the subscription
currently logged in to [Azure CLI][].

It uses the following environment variables to choose names:

* REPO\_NAME: GitHub repo in form `organization/repo`.
* AZURE\_BASE\_NAME: Prefix for Azure resources.
* AZURE\_DEFAULT\_LOCATION: Location for Azure resources.
* GH\_TOKEN: A GitHub [personal access token](https://github.com/settings/tokens)

These names can be specified in a .env file in the root of the package. If a
`.env` file isn't found, `.env.tpl` is copied to `.env` and used.

Explicit parameters can also be passed, see comments at beginning of
[setup.sh][] for details.

[Azure CLI]: https://github.com/Azure/azure-cli
[setup.sh]: ./setup.sh
33 changes: 33 additions & 0 deletions apps/webapp-source/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"fmt"
"golang.org/x/net/html"
"log"
"net/http"
"os"
)

func main() {
// find port selected by service, default to 8080
var port string
var found bool
port, found = os.LookupEnv("PORT")
if found == true {
// prepend a colon
port = fmt.Sprintf(":%s", port)
} else {
port = ":8080"
}

http.HandleFunc("/", tester)

log.Fatal(http.ListenAndServe(port, nil))
}

// tester
func tester(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello %s!", html.EscapeString(
r.URL.Query().Get("name")))
log.Printf("request received, details follow:\n%+v\n", r)
}
15 changes: 15 additions & 0 deletions apps/webapp-source/scripts/rm_helpers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function ensure_group () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function definition should just be rolled into the setup.sh script, unless it's intended to be used elsewhere - in that case it should go into some kind of generic utils package. If it is it still needs to be deployable.

local group_name=$1
local group_id

group_id=$(az group show --name $group_name --query 'id' --output tsv 2> /dev/null)

if [[ -z $group_id ]]; then
group_id=$(az group create \
--name $group_name \
--location $location \
--query 'id' --output tsv)
fi
echo $group_id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide more context. It might also be useful to echo the group name rather than the group ID.

}

110 changes: 110 additions & 0 deletions apps/webapp-source/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env bash

######
# setup.sh
#
# Sets up infrastructure for a continuously-built and deployed container-based
# web app for Azure App Service.
#
# $1: repo_name: Name of GitHub repo, e.g. joshgav/go-sample.
# $2: base_name: A default prefix for all Azure resources. Defaults to $AZURE_BASE_NAME.
# $3: app_name: Name of App Service web app to use or create. Defaults to
# "{AZURE_BASE_NAME}-webapp".
# $4: plan_name: Name of App Service plan to use or create. Defaults to
# "{AZURE_BASE_NAME}-plan".
# $5: group_name: Name of Azure resource group to use for all resources.
# Defaults to "${AZURE_BASE_NAME-group".
# $6: location: Name of Azure location to use for all resources. Defaults to
# $AZURE_DEFAULT_LOCATION.
# $7: gh_token: A GitHub personal access token to set up continuous
# integration. Defaults to $GH_TOKEN.
#####

__filename=${BASH_SOURCE[0]}
__dirname=$(cd $(dirname ${__filename}) && pwd)
__root=${__dirname}
if [[ ! -f "${__root}/.env" ]]; then cp "${__root}/.env.tpl" "${__root}/.env"; fi
if [[ -f "${__root}/.env" ]]; then source "${__root}/.env"; fi
source "${__dirname}/scripts/rm_helpers.sh" # for ensure_group

repo_name=${1:-${REPO_NAME}}
base_name=${2:-"${AZURE_BASE_NAME}"}
app_name=${3:-"${base_name}-webapp"}
plan_name=${4:-"${base_name}-plan"}
group_name=${5:-"${base_name}-group"}
location=${6:-${AZURE_DEFAULT_LOCATION}}
gh_token=${7:-${GH_TOKEN}}

scratch_runtime_id='node|8.1'
go_runtime_id='go|1'
url_suffix=azurewebsites.net
# as specified here:
# <https://github.com/projectkudu/KuduScript/blob/master/lib/templates/deploy.bash.go.template#L8>
binary_name=app

# errors
declare -i err_nogithubtoken=102
if [[ -z ${gh_token} ]]; then
echo 'specify a GitHub personal access token in the env var `GH_TOKEN`' \
'to set up continuous integration'
exit $err_nogithubtoken
fi

####
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We get some general output here, but maybe run the commands with --verbose so that more progress information from the CLI is displayed?


## ensure groups
ensure_group $group_name

## ensure App Service plan
plan_id=$(az appservice plan show \
--name ${plan_name} \
--resource-group ${group_name} \
--output tsv --query id)

if [[ -z $plan_id ]]; then
plan_id=$(az appservice plan create \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember right, this creates a plan with a basic S1 tier (FREE is not allowed for Linux containers for some reason) so users should be advised to clean up when finished. Maybe a cleanup script should be provided as well?

--name ${plan_name} \
--resource-group ${group_name} \
--location $location \
--is-linux \
--output tsv --query id)
fi
echo "ensured plan $plan_id"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird verb choice here. Change to Created or Set maybe?


## ensure Web App
webapp_id=$(az webapp show \
--name ${app_name} \
--resource-group ${group_name} \
--output tsv --query id 2> /dev/null)

# Go build support is behind a flag, so we specify a scratch runtime name and then change it
# when out from flag, use these options in `create` command
# --deployment-source-url "https://github.com/${repo_name}.git" \
# --deployment-source-branch "master" \
if [[ -z $webapp_id ]]; then
webapp_id=$(az webapp create \
--name "$app_name" \
--plan ${plan_id} \
--resource-group ${group_name} \
--runtime "${scratch_runtime_id}" \
--output tsv --query 'id')
fi

config_id=$(az webapp config set \
--ids $webapp_id \
--linux-fx-version "${go_runtime_id}" \
--output tsv --query 'id')

# see note above, remove when Go support is out from behind flag
source_config_id=$(az webapp deployment source config \
--ids $webapp_id \
--repo-url "https://github.com/${repo_name}" \
--branch 'master' \
--git-token ${gh_token} \
--repository-type github \
--output tsv --query 'id')
echo "ensured web app: $webapp_id"

## ensure operation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an echo here to indicate that the test is being run. Maybe:

echo "Testing deployment..."

curl -L "https://${app_name}.${url_suffix}/?name=gopherman"
echo ""