はじめに
GAE goな環境で開発しているときに、
$ goapp serve app.yaml
という感じで使うわけですが、そもそもgoappってなんぞやという事を軽くまとめてみました。
- goappコマンドの簡単な紹介
- goappコマンドとは
- goappコマンドのinstall
- goapp serveコマンドの挙動
1. goappコマンドの簡単な紹介
goappコマンドは、GAE goな環境で開発する際に使うコマンドです。
例えば、以下はローカルでGAEアプリを起動する際の例です
$ goapp serve app.yaml
INFO 2018-05-02 14:45:49,257 devappserver2.py:120] Skipping SDK update check.
INFO 2018-05-02 14:45:49,353 api_server.py:274] Starting API server at: http://localhost:51983
INFO 2018-05-02 14:45:49,360 dispatcher.py:270] Starting module "Hoge" running at: http://localhost:8080
INFO 2018-05-02 14:45:49,365 admin_server.py:152] Starting admin server at: http://localhost:8000
WARNING 2018-05-02 14:45:49,367 devappserver2.py:215] No default module found. Ignoring.
/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/mtime_file_watcher.py:182: UserWarning: There are too many files in your application for changes in all of them to be monitored. You may have to restart the development server to see some changes to your files.
'There are too many files in your application for '
2. goappコマンドとは
goappコマンドとは、自分なりの言葉で表現すると
Google Cloud SDKの一部である
gcloudでインストールするコンポーネントの1つに含まれるコマンド
です。(文章がわかりずらくてすみません)
gcloud/SDK/コンポーネントあたりについては別に書いてみたのでご参考ください。
【GAE】Google Cloud SDKとgcloudとコンポーネント(components)とgoappをおさらい
以降で、goappについて掘り下げます
GAEとgo
GAE goな環境で開発する
と記載しましたが、
GAEはgoを内包しています。
ためしにversionを見てみます。
PCにインストールしたgoとversionが違う事がわかります
# goappのversionと
# 内包されているgoのversionを確認
$ goapp version
go version 1.8.5 (appengine-1.9.68) darwin/amd64
# PCのgoのversionを確認
$ go version
go version go1.6.2 darwin/amd64
goappのコマンド
helpを見てみるとgoそのもののhelpと似ています
goapp help
$ goapp help
Go is a tool for managing Go source code.
Usage:
goapp command [arguments]
The commands are:
serve starts a local development App Engine server
deploy deploys your application to App Engine
build compile packages and dependencies
clean remove object files
doc show documentation for package or symbol
env print Go environment information
bug start a bug report
fix run go tool fix on packages
fmt run gofmt on package sources
generate generate Go files by processing source
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet run go tool vet on packages
Use "goapp help [command]" for more information about a command.
Additional help topics:
c calling between Go and C
buildmode description of build modes
filetype file types
gopath GOPATH environment variable
environment environment variables
importpath import path syntax
packages description of package lists
testflag description of testing flags
testfunc description of testing functions
Use "goapp help [topic]" for more information about that topic.
go help
$ go help
Go is a tool for managing Go source code.
Usage:
go command [arguments]
The commands are:
build compile packages and dependencies
clean remove object files
doc show documentation for package or symbol
env print Go environment information
fix run go tool fix on packages
fmt run gofmt on package sources
generate generate Go files by processing source
get download and install packages and dependencies
install compile and install packages and dependencies
list list packages
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet run go tool vet on packages
Use "go help [command]" for more information about a command.
Additional help topics:
c calling between Go and C
buildmode description of build modes
filetype file types
gopath GOPATH environment variable
environment environment variables
importpath import path syntax
packages description of package lists
testflag description of testing flags
testfunc description of testing functions
Use "go help [topic]" for more information about that topic.
goのラッパー的な要素に+αして
GAE goな環境で開発する際に便利な
serv
やdeploy
やbug
などを加えたといった感じです。
3. goappコマンドのinstall
goappコマンドは直接インストールするわけではありません。
2. goappコマンドとは
に記載しましたが、
gcloudコンポーネント群のうち、app-engine-go
というコンポーネントに含まれるコマンドです。
ここではgoappについてのみ記載します。
gcloudについては公式がわかりやすいです
gcloud の概要
# インストールされる場所を確認
$ gcloud info | grep Installation
Installation Root: [/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk]
Installation Properties: [/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/properties]
# gcloud経由でapp-engine-goをインストールする
$ gcloud components install app-engine-go
# 確認すると
# /usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine
# というパスに入っている
$ ls -l /usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/*goapp*
-rwxr-xr-x+ 1 tweeeety tweeeety 5109 5 2 22:08 /usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/goapp
# パスを追加
$ export PATH=$PATH:/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine
# 実行権限がないので追加
$ chmod +x /usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/goapp
# 使えるようになる
$ goapp version
go version 1.8.5 (appengine-1.9.68) darwin/amd64
app-engine-goでインストールされるもの
/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine
配下には
すでにいくつかのリソースが配置されてますが、
gcloud components install app-engine-go
によって以下が追加されます。
-rw-r--r--+ 1 tweeeety tweeeety 5949 3 17 16:55 LICENSE.golang
-rw-r--r--+ 1 tweeeety tweeeety 17738 3 17 16:59 RELEASE_NOTES.golang
-rw-r--r--+ 1 tweeeety tweeeety 248 3 17 16:55 VERSION.golang
-rwxr-xr-x+ 1 tweeeety tweeeety 3679424 3 17 17:00 go-app-stager
-rw-r--r--+ 1 tweeeety tweeeety 4798 3 17 16:59 goapp
-rw-r--r--+ 1 tweeeety tweeeety 4798 3 17 16:59 godoc
-rw-r--r--+ 1 tweeeety tweeeety 4798 3 17 16:59 gofmt
drwxr-xr-x+ 2 tweeeety tweeeety 68 3 17 17:01 gopath
drwxr-xr-x+ 14 tweeeety tweeeety 476 5 2 21:30 goroot-1.6
drwxr-xr-x+ 14 tweeeety tweeeety 476 5 2 21:30 goroot-1.8
drwxr-xr-x+ 14 tweeeety tweeeety 476 5 2 21:30 goroot-1.9
まぁ、go関連で納得という感じですね
4. goapp serveコマンドの挙動
goappというかgoapp serve
の挙動です。
help
まずはhelpを見てみます。
$ goapp serve --help
usage: serve [serve flags] [application_dir | package | yaml_files...]
Serve launches your application on a local development App Engine server.
The argument to this command should be your application's root directory or a
single package which contains an app.yaml file. If you are using the Modules
feature, then you should pass multiple YAML files to serve, rather than a
directory, to specify which modules to serve. If no arguments are provided,
serve looks in your current directory for an app.yaml file.
The -host flag controls the host name to which application modules should bind
(default localhost).
The -port flag controls the lowest port to which application modules should
bind (default 8080).
The -use_mtime_file_watcher flag causes the development server to use mtime
polling for detecting source code changes, as opposed to inotify watches.
The -admin_port flag controls the port to which the admin server should bind
(default 8000).
The -clear_datastore flag clears the local datastore on startup.
The -debug flag builds the binary with debug information so that gdb or delve
can be used (default false).
This command wraps the dev_appserver.py command provided as part of the
App Engine SDK. For help using that command directly, run:
./dev_appserver.py --help
最後に書いてありますが、dev_appserver.py
のラッパーであることがわかります。
また、オプションでportなどが指定できます。
goapp コマンドの中身
中身はpythonで書かれています。
100行ちょいなので読めるレベルです。
"""Convenience wrapper for starting a Go tool in the App Engine SDK."""
from __future__ import print_function
import argparse
import os
import sys
SDK_BASE = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
def FixGooglePath():
"""Adds the python libraries vendored with the SDK to sys.path.
This allows us to run gotool.py directly and import the dependencies under the
google/ directory.
"""
import wrapper_util
sys.path = wrapper_util.Paths(SDK_BASE).v1_extra_paths + sys.path
if 'google' in sys.modules:
google_path = os.path.join(os.path.dirname(__file__), 'google')
google_module = sys.modules['google']
google_module.__path__.append(google_path)
if not hasattr(google_module, '__file__') or not google_module.__file__:
google_module.__file__ = os.path.join(google_path, '__init__.py')
FixGooglePath()
from google.appengine.api import appinfo_includes
from google.appengine.tools.devappserver2.go import goroots
def GetArgsAndArgv():
"""Parses command-line arguments and strips out flags the control this file.
Returns:
Tuple of (flags, rem) where "flags" is a map of key,value flags pairs
and "rem" is a list that strips the used flags and acts as a new
replacement for sys.argv.
"""
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'--dev_appserver', default=os.path.join(SDK_BASE, 'dev_appserver.py'))
parser.add_argument(
'--print-command', dest='print_command', default=False,
action='store_true')
namespace, rest = parser.parse_known_args()
flags = vars(namespace)
rem = [sys.argv[0]]
rem.extend(rest)
return flags, rem
def GetAppYamlPaths(gopath, app_path):
"""Generate possible paths to app.yaml.
Args:
gopath: String path to gopath directory.
app_path: String path to the app.
Yields:
Possible paths for app.yaml from app_path up to the gopath root.
"""
gopath = os.path.abspath(gopath)
app_path = os.path.abspath(app_path)
if app_path.endswith('.yaml'):
yield app_path
raise StopIteration()
yield os.path.join(app_path, 'app.yaml')
while app_path.startswith(gopath) and app_path != gopath:
app_path = os.path.abspath(os.path.join(app_path, '..'))
yield os.path.join(app_path, 'app.yaml')
def _ParseAppYaml(file_name):
with open(file_name, 'r') as f:
return appinfo_includes.Parse(f)
def GetGoRoot(gopath, argv):
"""Find the goroot.
Args:
gopath: String path to gopath directory.
argv: A list of strings that looks like sys.argv. The last element of this
list is checked to see if it's a valid path and, if so, is included in the
search.
Returns:
The path to the correct goroot for the project.
"""
paths = [os.path.realpath(os.path.curdir)]
if len(argv) > 2:
app_path = argv[-1]
if not app_path.startswith('-'):
app_path = app_path.rstrip('...')
paths += [os.path.realpath(app_path)]
for path in paths:
for app_yaml_path in GetAppYamlPaths(gopath, path):
if os.path.exists(app_yaml_path):
app_yaml = _ParseAppYaml(app_yaml_path)
if hasattr(app_yaml, 'api_version'):
return os.path.join(SDK_BASE, goroots.GOROOTS[app_yaml.api_version])
return os.path.join(SDK_BASE, goroots.GOROOTS['go1'])
if __name__ == '__main__':
vals, new_argv = GetArgsAndArgv()
tool = os.path.basename(__file__)
if not os.environ.get('GOPATH'):
os.environ['GOPATH'] = os.path.join(SDK_BASE, 'gopath')
os.environ['APPENGINE_DEV_APPSERVER'] = vals['dev_appserver']
goroot = GetGoRoot(os.environ['GOPATH'], sys.argv)
os.environ['GOROOT'] = goroot
tool = os.path.join(goroot, 'bin', tool)
for e in ('GOARCH', 'GOBIN', 'GOOS'):
os.environ.pop(e, None)
new_argv[0] = tool
if vals['print_command']:
print(' '.join(new_argv))
else:
os.execve(tool, new_argv, os.environ)
if __name__ == '__main__':
がメインです。
tool, new_argv, os.environらを組み立てて
最後のos.execveで実行しています。
os.execve
は、現在のプロセスを置き換える形で新たなプログラムを実行します。
os.execve(path, args, env)
os.execveの前に登場する各変数をdumpしてみるとこんな値が入ってます
tool
/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/goroot-1.8/bin/goapp
vals
{'dev_appserver': '/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/dev_appserver.py','print_command': False}
new_argv
['/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/goapp',
'serve',
'app.yaml']
os.environ
{
'DIRENV_DIR': '-/Users/tweeeety/gitrepos/Hoge',
'GOPATH': '/Users/tweeeety/gitrepos/Hoge',
'PERL_CPANM_OPT': '--mirror http://cpan.metacpan.org',
'APPENGINE_DEV_APPSERVER': '/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/dev_appserver.py',
'GOROOT': '/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/goroot-1.8',
'DIRENV_DIFF': 'hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge',
'TERM_PROGRAM_VERSION': '326',
'SHELL': '/bin/bash',
'LOGNAME': 'tweeeety',
'USER': 'tweeeety',
'HOME': '/Users/tweeeety',
'PATH': '/usr/local/opt/imagemagick@6/bin:/Users/tweeeety/.plenv/shims:/Users/tweeeety/.plenv/bin:/Users/tweeeety/.nodebrew/current/bin:/Users/tweeeety/.mysqlenv/mysqls/5.1.69/bin:/Users/tweeeety/.mysqlenv/bin:/Users/tweeeety/.mysqlenv/shims:/Users/tweeeety/.mysqlenv/mysql-build/bin:/usr/local/sbin:/Users/tweeeety/.mysqlenv/bin:/Users/tweeeety/.mysqlenv/shims:/Users/tweeeety/.mysqlenv/mysql-build/bin:/usr/local/heroku/bin:/Users/tweeeety/.chefdk/gem/ruby/2.1.0/bin:/opt/chefdk/bin:/Users/tweeeety/.rbenv/shims:/Users/tweeeety/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/go/bin:/sbin:/usr/sbin:/Users/tweeeety/local/bin:/usr/local/opt/go/libexec/bin:/Users/tweeeety/.go/bin:/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine',
'PS1': '[\\[\\033[40;1;32m\\]\\w \\[\\033[40;2;37m\\]\\t\\[\\033[0m\\]]$ ',
'TERM_PROGRAM': 'Apple_Terminal',
'LANG': 'ja_JP.UTF-8',
'TERM': 'xterm-256color',
'Apple_PubSub_Socket_Render': '/tmp/launch-95EcNN/Render',
'SHLVL': '1',
'TEST_MYSQL_SOCK': '/tmp/mysql.sock',
'SECURITYSESSIONID': 'hoge',
'RBENV_SHELL': 'bash',
'EDITOR': 'vim',
'TERM_SESSION_ID': 'hogehogehogehoge',
'SSH_AUTH_SOCK': '/tmp/launch-Z5iV4t/Listeners',
'MODULE': 'Hoge',
'TEST_LOCAL_MYSQL_SOCK': '/tmp/mysql.sock',
'_': '/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/platform/google_appengine/goapp',
'TMPDIR': '/var/folders/5x/2wrjp7td5bj9cprs4c4cwxswqmbl26/T/',
'PERL5LIB': '/Users/tweeeety/testApp/Sample/lib/:',
'PLENV_SHELL': 'bash',
'OLDPWD': '/Users/tweeeety/gitrepos/Fuga',
'__CF_USER_TEXT_ENCODING': '0x2F45CC46:1:14',
'PWD': '/Users/tweeeety/gitrepos/Hoge',
'DIRENV_WATCHES': 'hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge',
'__CHECKFIX1436934': '1'
}
おわり
なんとなくわかった気になりました\(^o^)/