Gcloud 函数部署找不到 Golang 模板文件
Gcloud functions deployment doesn't find Golang template files
我写了一些 Golang 代码,在我的本地机器上测试时可以正常工作。当我将其部署为 Google 云函数时,它失败了,因为它无法打开模板文件。失败的代码行是:
t, err := template.New("list.gohtml").ParseFiles("list.gohtml")
此调用后 err
设置为 open list.gohtml: no such file or directory
该文件与go源文件在同一目录下,未在.gcloudignore
或.gitignore
中列出。 gcloud 函数文档说目录中的所有文件都将被上传,除非在这些忽略文件之一中列出,如果我 运行 gcloud meta list-files-for-upload
那么文件 list.gohtml
是 包含在显示的列表中。
是否有一些神奇的文件夹布局可以实现此功能,或者 gcloud functions deploy
命令的选项?
我创建了一个枚举上传文件的函数:
func L(w http.ResponseWriter, r *http.Request) {
var files []string
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
files = append(files, path)
return nil
})
if err != nil {
panic(err)
}
for _, file := range files {
fmt.Fprintln(w, file)
}
}
输出:
.
go.mod
go.sum
main.go
serverless_function_source_code
serverless_function_source_code/f.go
serverless_function_source_code/go.mod
serverless_function_source_code/test.tmpl
重写使用模板的函数:
tmpl, err := template.New("test.tmpl").ParseFiles("serverless_function_source_code/test.tmpl")
有效!
但是,这是未记录的并且是黑客 :-(
另一种方法是将模板作为字符串嵌入到 Golang 文件中。
我建议在 Google 的 Cloud Functions
问题跟踪器上提交功能请求
根据@DazWilkin 的回复,我现在在服务函数开始时调用下面的函数。
而不是将路径硬连接到模板文件名中(这会导致在本地测试时失败),这只是检查当前文件下面是否存在 Gcloud source file directory,如果存在,则将其设为工作目录,因此文件解析现在将与在本地测试时完全一样。
import "os"
const gcloudFuncSourceDir = "serverless_function_source_code"
func fixDir() {
fileInfo, err := os.Stat(gcloudFuncSourceDir)
if err == nil && fileInfo.IsDir() {
_ = os.Chdir(gcloudFuncSourceDir)
}
}
如果没有 go.mod
文件(换句话说,如果未启用模块),则行为会改变并且文件位于 src/<package-name>/
下,而不是 serverless_function_source_code/
下。用于驱动云函数的 main.go
文件位于 src/serverless_function_app/main/main.go
.
例如:
我有一个包含以下两个文件的 Go 应用程序:
listfiles/file_system.go
listfiles/tmpl/letter.html
我用这些命令部署了它:
cd listfiles
gcloud functions deploy ListFiles --runtime go113 --trigger-http --allow-unauthenticated
结果是当前目录设置为/srv
。 /srv
下面是一个典型的 GOROOT 树,其中包含 src/
和 pkg/
目录。例如:
.googlebuild
.googlebuild/source-code.tar.gz
pkg
pkg/linux_amd64
pkg/linux_amd64/github.com
pkg/linux_amd64/github.com/GoogleCloudPlatform
pkg/linux_amd64/github.com/GoogleCloudPlatform/functions-framework-go
pkg/linux_amd64/github.com/GoogleCloudPlatform/functions-framework-go/funcframework.a
src
src/cloud.google.com
src/cloud.google.com/go
src/cloud.google.com/go/.git
src/cloud.google.com/go/.git/HEAD
src/cloud.google.com/go/.git/branches
[... snip...]
src/go.uber.org/zap/zaptest/testingt_test.go
src/go.uber.org/zap/zaptest/timeout.go
src/go.uber.org/zap/zaptest/timeout_test.go
src/go.uber.org/zap/zaptest/writer.go
src/go.uber.org/zap/zaptest/writer_test.go
src/listfiles
src/listfiles/file_system.go
src/listfiles/tmpl
src/listfiles/tmpl/letter.html
src/serverless_function_app
src/serverless_function_app/main
src/serverless_function_app/main/main.go
我写了一些 Golang 代码,在我的本地机器上测试时可以正常工作。当我将其部署为 Google 云函数时,它失败了,因为它无法打开模板文件。失败的代码行是:
t, err := template.New("list.gohtml").ParseFiles("list.gohtml")
此调用后 err
设置为 open list.gohtml: no such file or directory
该文件与go源文件在同一目录下,未在.gcloudignore
或.gitignore
中列出。 gcloud 函数文档说目录中的所有文件都将被上传,除非在这些忽略文件之一中列出,如果我 运行 gcloud meta list-files-for-upload
那么文件 list.gohtml
是 包含在显示的列表中。
是否有一些神奇的文件夹布局可以实现此功能,或者 gcloud functions deploy
命令的选项?
我创建了一个枚举上传文件的函数:
func L(w http.ResponseWriter, r *http.Request) {
var files []string
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
files = append(files, path)
return nil
})
if err != nil {
panic(err)
}
for _, file := range files {
fmt.Fprintln(w, file)
}
}
输出:
.
go.mod
go.sum
main.go
serverless_function_source_code
serverless_function_source_code/f.go
serverless_function_source_code/go.mod
serverless_function_source_code/test.tmpl
重写使用模板的函数:
tmpl, err := template.New("test.tmpl").ParseFiles("serverless_function_source_code/test.tmpl")
有效!
但是,这是未记录的并且是黑客 :-(
另一种方法是将模板作为字符串嵌入到 Golang 文件中。
我建议在 Google 的 Cloud Functions
问题跟踪器上提交功能请求根据@DazWilkin 的回复,我现在在服务函数开始时调用下面的函数。
而不是将路径硬连接到模板文件名中(这会导致在本地测试时失败),这只是检查当前文件下面是否存在 Gcloud source file directory,如果存在,则将其设为工作目录,因此文件解析现在将与在本地测试时完全一样。
import "os"
const gcloudFuncSourceDir = "serverless_function_source_code"
func fixDir() {
fileInfo, err := os.Stat(gcloudFuncSourceDir)
if err == nil && fileInfo.IsDir() {
_ = os.Chdir(gcloudFuncSourceDir)
}
}
如果没有 go.mod
文件(换句话说,如果未启用模块),则行为会改变并且文件位于 src/<package-name>/
下,而不是 serverless_function_source_code/
下。用于驱动云函数的 main.go
文件位于 src/serverless_function_app/main/main.go
.
例如:
我有一个包含以下两个文件的 Go 应用程序:
listfiles/file_system.go
listfiles/tmpl/letter.html
我用这些命令部署了它:
cd listfiles
gcloud functions deploy ListFiles --runtime go113 --trigger-http --allow-unauthenticated
结果是当前目录设置为/srv
。 /srv
下面是一个典型的 GOROOT 树,其中包含 src/
和 pkg/
目录。例如:
.googlebuild
.googlebuild/source-code.tar.gz
pkg
pkg/linux_amd64
pkg/linux_amd64/github.com
pkg/linux_amd64/github.com/GoogleCloudPlatform
pkg/linux_amd64/github.com/GoogleCloudPlatform/functions-framework-go
pkg/linux_amd64/github.com/GoogleCloudPlatform/functions-framework-go/funcframework.a
src
src/cloud.google.com
src/cloud.google.com/go
src/cloud.google.com/go/.git
src/cloud.google.com/go/.git/HEAD
src/cloud.google.com/go/.git/branches
[... snip...]
src/go.uber.org/zap/zaptest/testingt_test.go
src/go.uber.org/zap/zaptest/timeout.go
src/go.uber.org/zap/zaptest/timeout_test.go
src/go.uber.org/zap/zaptest/writer.go
src/go.uber.org/zap/zaptest/writer_test.go
src/listfiles
src/listfiles/file_system.go
src/listfiles/tmpl
src/listfiles/tmpl/letter.html
src/serverless_function_app
src/serverless_function_app/main
src/serverless_function_app/main/main.go