英文:
go test causes no such file or directory
问题
我正在使用Go创建一个RESTful API,并使用JWT来保护我的应用程序。我正在使用ECDSA算法,因此我有私钥和公钥对。为了创建JWT令牌,我需要读取它们。
首先,这是我的项目结构:
.
├── Dockerfile
├── Makefile
├── config
│ └── config.go
├── config.json
├── data
│ ├── init.sql
│ ├── keys
│ │ ├── 256
│ │ │ ├── keys-256.md
│ │ │ ├── private-key.pem
│ │ │ └── public-key.pem
│ │ └── 512
│ │ ├── keys-512.md
│ │ ├── private-key.pem
│ │ └── public-key.pem
│ └── ....
├── go.mod
├── go.sum
├── handler
│ ├── auth.go
│ ├── auth_test.go
│ ├── healthcheck.go
│ └── healthcheck_test.go
├── main.go
└── security
└── token
├── generator.go
└── parser.go
这是我读取私钥文件的源文件,正如你所看到的,我在Go中使用了init函数,因此我将读取密钥文件一次,并在需要时重复使用它。
generator.go
package security
import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
"time"
"github.com/golang-jwt/jwt"
// ...
)
var privateKeyPath = "data/keys/512/private-key.pem"
var privateKey *ecdsa.PrivateKey
func init() {
printWorkingDirectory()
privateKey = getECDSAPrivateKey(readPrivateKeyFile())
}
func getECDSAPrivateKey(data []byte) *ecdsa.PrivateKey {
ecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(data);
if err != nil {
log.Fatalf("Unable to parse ECDSA private key: %v", err)
}
return ecdsaKey
}
func readPrivateKeyFile() []byte {
data, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
log.Fatalf("Can not read private key file: %v", err)
}
return data;
}
func printWorkingDirectory() {
mydir, err := os.Getwd()
if err != nil {
fmt.Println(err)
}
fmt.Println("Working directory: ", mydir)
}
//...
密钥路径是相对于项目的根目录的 var privateKeyPath = "data/keys/512/private-key.pem"
当我在根目录中运行 go run .
时,一切正常。
当我运行 go test ./...
时,Golang会创建一个临时目录,并在其中运行我的测试,这基本上会导致问题。以下是 go test ./...
的结果:
✗ go test -v ./...
Working directory is: /var/folders/wk/ncyvbtdj3w57cr5j1v46x7dr39dz_l/T/go-build4211274972/b150
Initing db manger
2021/10/28 19:13:47 Can not read private key file: open data/keys/512/private-key.pem: no such file or directory
FAIL mydomain/myprojectname/handler 1.274s
实际上,我是一名Java开发人员,对Go还不太熟悉。
在Java中,我们有 classpath
,它可以解决这个问题。
我不想使用绝对路径,因为我有Dockerfile,如果我在本地计算机上使用绝对路径,那么在Docker容器中使用时会有问题。
我的问题是:
如何使用相对路径使其适用于 go run
和 go test
命令。
英文:
I'm creating a RESTFull API in Go and securing my application with JWT. I'm using ECDSA algorithm so I have private
and public
key pair. In order to create a JWT token I have to read them.
First of all this is my project structure:
.
├── Dockerfile
├── Makefile
├── config
│   └── config.go
├── config.json
├── data
│   ├── init.sql
│   ├── keys
│   │   ├── 256
│   │   │   ├── keys-256.md
│   │   │   ├── private-key.pem
│   │   │   └── public-key.pem
│   │   └── 512
│   │   ├── keys-512.md
│   │   ├── private-key.pem
│   │   └── public-key.pem
│   └── ....
├── go.mod
├── go.sum
├── handler
│   ├── auth.go
│   ├── auth_test.go
│   ├── healthcheck.go
│   └── healthcheck_test.go
│  
├── main.go
└── security
   └── token
   ├── generator.go
   └── parser.go
This is the source file where I read private key file as you can see I'm using init function in go so I'll read the key file once and use it every time I need it.
generator.go
package security
import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
"time"
"github.com/golang-jwt/jwt"
// ...
)
var privateKeyPath = "data/keys/512/private-key.pem"
var privateKey *ecdsa.PrivateKey
func init() {
printWorkingDirectory()
privateKey = getECDSAPrivateKey(readPrivateKeyFile())
}
func getECDSAPrivateKey(data []byte) *ecdsa.PrivateKey {
ecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(data);
if err != nil {
log.Fatalf("Unable to parse ECDSA private key: %v", err)
}
return ecdsaKey
}
func readPrivateKeyFile() []byte {
data, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
log.Fatalf("Can not read private key file: %v", err)
}
return data;
}
func printWorkingDirectory() {
mydir, err := os.Getwd()
if err != nil {
fmt.Println(err)
}
fmt.Println("Working directory: ", mydir)
}
//...
Key path is relative to root directory of project var privateKeyPath = "data/keys/512/private-key.pem"
When I run go run .
in the root directory, everything fine.
When I run go test ./...
Golang creates a temporary directory and runs my test in there basically this causes a problem.
Here is the result of go test ./...
:
✗ go test -v ./...
Working directory is: /var/folders/wk/ncyvbtdj3w57cr5j1v46x7dr39dz_l/T/go-build4211274972/b150
Initing db manger
2021/10/28 19:13:47 Can not read private key file: open data/keys/512/private-key.pem: no such file or directory
FAIL mydomain/myprojectname/handler 1.274s
Actually I'm a Java Developer and I'm new in Go
Simply in Java we have classpath
and it saves the day.
I don't want to use absolute path
because I have Dockerfile and if I use absolute path in my local computer it will hurt me in docker container.
My question is:
How can I use a relative path which works with go run
and go test
commands.
答案1
得分: 1
你面临的问题是,当你运行go run
或者运行你构建的应用程序时,工作目录是从你的环境继承的。但是当测试一个包时,工作目录始终是包目录。
一个快速的解决方案是,在你的security
目录中将data
目录复制一份作为测试装置,这样你也可以在测试中使用特定的密钥。
一个更好的解决方案是思考如何在生产环境中加载密钥。你的security
包中的init()
函数非常不灵活,因为:
- 在生产环境中,你的密钥必须位于相对于工作目录的特定位置。
- 你无法影响测试中
init()
函数的行为,因为init()
函数总是在测试开始之前运行。
我的建议是,不要使用init()
函数,而是在你从main
实例化security
包中的类/类时,将字面上的密钥或密钥的路径传递给它们。这样,在生产环境中,你的密钥可以位于任何位置,而你的测试可以指定使用源代码树中与该包目录相关的密钥。
英文:
The problem you are facing is because when you run go run
, or for that matter when you run your built application, the working directory is inherited from your environment. But when a package is tested, the working directory is always the package directory.
One quick solution would be to have a copy of your data
directory inside your security
directory as a test fixture, this would also allow you to use a specific key for testing.
A better solution is to think about how you will load your key in production. This init()
function in your security
package is quite inflexible because:
- In production, your key must be in a specific location relative to the working directory only.
- You cannot influence the behaviour of the
init()
function in tests becauseinit()
is always run before tests start.
My recommendation would be that rather than using init()
, you pass either the literal key, or a path to the key into class/classes in the security
package from main
when you instantiate them. This way your key can be located anywhere when in production and your tests can be directed to use a key in the source tree relative to that package's directory.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论