英文:
Golang panic: interface conversion: interface {} is nil, not string
问题
我已经尝试创建CA配置文件、证书和私钥,我正在使用Go语言中的cfssl命令,并尝试从以下命令模拟相同的命令:
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
以下是我的代码片段:
package main
import (
    "bytes"
    "fmt"
    "io"
    "os/exec"
    "github.com/cloudflare/cfssl/log"
    "encoding/json"
    "io/ioutil"
    "os"
    "github.com/cloudflare/cfssl/cli"
    "github.com/cloudflare/cfssl/cli/bundle"
    "github.com/cloudflare/cfssl/cli/certinfo"
    "github.com/cloudflare/cfssl/cli/gencert"
    "github.com/cloudflare/cfssl/cli/gencrl"
    "github.com/cloudflare/cfssl/cli/genkey"
    "github.com/cloudflare/cfssl/cli/info"
    "github.com/cloudflare/cfssl/cli/ocspdump"
    "github.com/cloudflare/cfssl/cli/ocsprefresh"
    "github.com/cloudflare/cfssl/cli/ocspserve"
    "github.com/cloudflare/cfssl/cli/ocspsign"
    printdefaults "github.com/cloudflare/cfssl/cli/printdefault"
    "github.com/cloudflare/cfssl/cli/revoke"
    "github.com/cloudflare/cfssl/cli/scan"
    "github.com/cloudflare/cfssl/cli/selfsign"
    "github.com/cloudflare/cfssl/cli/serve"
    "github.com/cloudflare/cfssl/cli/sign"
    "github.com/cloudflare/cfssl/cli/version"
    //  err "github.com/hyperledger/fabric/cop/errors"
    "google.golang.org/grpc"
)
func main() {
    var participantFile string = "key.pem"
    // file, err := os.Open("conf/ca-csr.json")
    // if err != nil {
    //  log.Fatalf("missing config file: %v", err)
    // }
    // defer file.Close()
    // s, err := ioutil.ReadAll(file)
    // if err != nil {
    //  log.Fatalf("could not read config file: %v", err)
    // }
    csr := `{
        "CN": "admin",
        "key": {
          "algo": "rsa",
          "size": 2048
        },
        "names": [
          {
            "C": "US",
            "L": "Portland",
            "O": "system:masters",
            "OU": "Kubernetes The Hard Way",
            "ST": "Oregon"
          }
        ]
      }`
    //csr := string([]byte(s[:]))
    GenCert(csr, participantFile)
}
var cfsslCmds = map[string]*cli.Command{
    "bundle":         bundle.Command,
    "certinfo":       certinfo.Command,
    "sign":           sign.Command,
    "serve":          serve.Command,
    "version":        version.Command,
    "genkey":         genkey.Command,
    "gencert":        gencert.Command,
    "gencrl":         gencrl.Command,
    "ocspdump":       ocspdump.Command,
    "ocsprefresh":    ocsprefresh.Command,
    "ocspsign":       ocspsign.Command,
    "ocspserve":      ocspserve.Command,
    "selfsign":       selfsign.Command,
    "scan":           scan.Command,
    "info":           info.Command,
    "print-defaults": printdefaults.Command,
    "revoke":         revoke.Command,
}
// CertMgr is the default certificate manager
type CertMgr struct {
    rootPath            string
    participantFilePath string
    cert                []byte
    grpcServer          *grpc.Server
}
type output struct {
    Cert string
}
type gencertOutput struct {
    cert []byte
    csr  []byte
    key  []byte
}
func GenCert(csr string, participantFile string) {
    var args []string
    gencertCmd := cfsslCmds["gencert"]
    var c cli.Config
    c.IsCA = true
    args = append(args, csr)
    out := ExecuteCommand(args, gencertCmd, c)
    var gencertOut map[string]interface{}
    json.Unmarshal([]byte(out), &gencertOut)
    var writeJSON output
    writeJSON.Cert = gencertOut["cert"].(string)
    jsonOut, _ := json.Marshal(writeJSON)
    ioutil.WriteFile(participantFile, jsonOut, 0644)
    return
}
func ExecuteCommand(args []string, command *cli.Command, c cli.Config) string {
    cfsslJSONCmd := exec.Command("cfssljson", "-bare")
    old := os.Stdout
    r, w, _ := os.Pipe()
    os.Stdout = w
    err := command.Main(args, c) // Execute command
    if err != nil {
        log.Error(err)
    }
    outC := make(chan string)
    // copy the output in a separate goroutine so printing can't block indefinitely
    var buf bytes.Buffer
    go func() {
        io.Copy(&buf, r)
        cfsslJSONCmd.Stdin = &buf
        outC <- buf.String()
    }()
    w.Close()
    out := <-outC
    outByte := []byte(out)
    tmpFile, _ := ioutil.TempFile("", "tmp")
    defer os.Remove(tmpFile.Name())
    if _, err = tmpFile.Write(outByte); err != nil {
        fmt.Println("err: ", err)
    }
    os.Stdin = tmpFile
    os.Stdout = old // restoring the real stdout
    err = cfsslJSONCmd.Run() // Execute cfssljson -bare <prefix>
    if err != nil {
        log.Error(err)
    }
    return out // To be used to store in participant file
}
我得到了以下错误:
[ERROR] open {
"CN": "admin",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:masters",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}: no such file or directory
2022/03/25 19:23:39 [ERROR] exit status 1
panic: interface conversion: interface {} is nil, not string
goroutine 1 [running]:
main.GenCert({0xb9179c, 0xda}, {0xb3a3f3, 0x7})
/home/Documents/git-repo/kunets-vpc-test/main.go:126 +0x229
main.main()
/home/Documents/git-repo/kunets-vpc-test/main.go:70 +0x31
任何帮助和建议将不胜感激,非常感谢!
<details>
<summary>英文:</summary>
I have tried to create CA configuration file, certificate and private key, I am using `cfssl` command in go and try to simulate same command from 
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
Here is my code snipped 
package main
import (
"bytes"
"fmt"
"io"
"os/exec"
"github.com/cloudflare/cfssl/log"
"encoding/json"
"io/ioutil"
"os"
"github.com/cloudflare/cfssl/cli"
"github.com/cloudflare/cfssl/cli/bundle"
"github.com/cloudflare/cfssl/cli/certinfo"
"github.com/cloudflare/cfssl/cli/gencert"
"github.com/cloudflare/cfssl/cli/gencrl"
"github.com/cloudflare/cfssl/cli/genkey"
"github.com/cloudflare/cfssl/cli/info"
"github.com/cloudflare/cfssl/cli/ocspdump"
"github.com/cloudflare/cfssl/cli/ocsprefresh"
"github.com/cloudflare/cfssl/cli/ocspserve"
"github.com/cloudflare/cfssl/cli/ocspsign"
printdefaults "github.com/cloudflare/cfssl/cli/printdefault"
"github.com/cloudflare/cfssl/cli/revoke"
"github.com/cloudflare/cfssl/cli/scan"
"github.com/cloudflare/cfssl/cli/selfsign"
"github.com/cloudflare/cfssl/cli/serve"
"github.com/cloudflare/cfssl/cli/sign"
"github.com/cloudflare/cfssl/cli/version"
//  err "github.com/hyperledger/fabric/cop/errors"
"google.golang.org/grpc"
)
func main() {
var participantFile string = "key.pem"
// file, err := os.Open("conf/ca-csr.json")
// if err != nil {
//  log.Fatalf("missing config file: %v", err)
// }
// defer file.Close()
// s, err := ioutil.ReadAll(file)
// if err != nil {
//  log.Fatalf("could not read config file: %v", err)
// }
csr := `{
"CN": "admin",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:masters",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}`
//csr := string([]byte(s[:]))
GenCert(csr, participantFile)
}
var cfsslCmds = map[string]*cli.Command{
"bundle":         bundle.Command,
"certinfo":       certinfo.Command,
"sign":           sign.Command,
"serve":          serve.Command,
"version":        version.Command,
"genkey":         genkey.Command,
"gencert":        gencert.Command,
"gencrl":         gencrl.Command,
"ocspdump":       ocspdump.Command,
"ocsprefresh":    ocsprefresh.Command,
"ocspsign":       ocspsign.Command,
"ocspserve":      ocspserve.Command,
"selfsign":       selfsign.Command,
"scan":           scan.Command,
"info":           info.Command,
"print-defaults": printdefaults.Command,
"revoke":         revoke.Command,
}
// CertMgr is the default certificate manager
type CertMgr struct {
rootPath            string
participantFilePath string
cert                []byte
grpcServer          *grpc.Server
}
type output struct {
Cert string
}
type gencertOutput struct {
cert []byte
csr  []byte
key  []byte
}
func GenCert(csr string, participantFile string) {
var args []string
gencertCmd := cfsslCmds["gencert"]
var c cli.Config
c.IsCA = true
args = append(args, csr)
out := ExecuteCommand(args, gencertCmd, c)
var gencertOut map[string]interface{}
json.Unmarshal([]byte(out), &gencertOut)
var writeJSON output
writeJSON.Cert = gencertOut["cert"].(string)
jsonOut, _ := json.Marshal(writeJSON)
ioutil.WriteFile(participantFile, jsonOut, 0644)
return
}
func ExecuteCommand(args []string, command *cli.Command, c cli.Config) string {
cfsslJSONCmd := exec.Command("cfssljson", "-bare")
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
err := command.Main(args, c) // Execute command
if err != nil {
log.Error(err)
}
outC := make(chan string)
// copy the output in a separate goroutine so printing can't block indefinitely
var buf bytes.Buffer
go func() {
io.Copy(&buf, r)
cfsslJSONCmd.Stdin = &buf
outC <- buf.String()
}()
w.Close()
out := <-outC
outByte := []byte(out)
tmpFile, _ := ioutil.TempFile("", "tmp")
defer os.Remove(tmpFile.Name())
if _, err = tmpFile.Write(outByte); err != nil {
fmt.Println("err: ", err)
}
os.Stdin = tmpFile
os.Stdout = old // restoring the real stdout
err = cfsslJSONCmd.Run() // Execute cfssljson -bare <prefix>
if err != nil {
log.Error(err)
}
return out // To be used to store in participant file
}
and I am getting that error
[ERROR] open {
"CN": "admin",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "system:masters",
"OU": "Kubernetes The Hard Way",
"ST": "Oregon"
}
]
}: no such file or directory
2022/03/25 19:23:39 [ERROR] exit status 1
panic: interface conversion: interface {} is nil, not string
goroutine 1 [running]:
main.GenCert({0xb9179c, 0xda}, {0xb3a3f3, 0x7})
/home/Documents/git-repo/kunets-vpc-test/main.go:126 +0x229
main.main()
/home/Documents/git-repo/kunets-vpc-test/main.go:70 +0x31
Any help advice would be appreciated, thank you very much! 
</details>
# 答案1
**得分**: 4
我认为你在 goroutine 运行时关闭了 w,并且以某种方式突然结束了进程或读取命令输出。尝试通过打印 "out" 来确认,如果是这个问题,可以使用 defer 关闭 w。
使用安全转换来避免 panic,并使用 spew.Dump(需要外部库)来检查数据。
或者使用调试器。
```go
variable, ok := something.(string)
if !ok {
// 无法转换为字符串,现在该怎么办???
}
英文:
I think you are closing the w while the goroutine is running and somehow it ends abruptly the process or reading the command output. Try confirm by print the “out” and if it is the issue, close w with defer
Use a safe convertion to avoid panic and use spew.Dump (need external library) to inspect the data.
Or use a debugger
variable, ok := something.(string)
if !ok {
  // cant convert to string, now what???
}
</details>
# 答案2
**得分**: 0
我在模拟时遇到了类似的问题。
```go
type MockSomethingRepository struct {
	mock.Mock
}
func (msr *MockSomethingRepository) FindById(id string) (*entity.Session, error) {
	args := msr.Called(id)
	return args.Get(0).(*entity.Session), args.Error(1)
}
然后我尝试像这样使用模拟对象:
mockSomethignRepository.On("FindById", "1").Return(nil, fmt.Errorf("something went wrong"))
我得到了类似的错误interface conversion: interface {} is *[]entity.Session, not []entity.Session。
我在https://go.dev/play/p/CLyY2y9-2VF找到了解决方案,所以我通过以下方式解决了问题:
type MockSomethingRepository struct {
	mock.Mock
}
func (msr *MockSomethingRepository) FindById(id string) (*entity.Session, error) {
	args := msr.Called(id)
	if args.Error(1) != nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*entity.Session), args.Error(1)
}
我没有解释,但它有效!
英文:
I was struggling with a similar problem when I was mocking.
type MockSomethingRepository struct {
mock.Mock
}
func (msr *MockSomethingRepository) FindById(id string) (*entity.Session, error) {
args := msr.Called(id)
return args.Get(0).(*entity.Session), args.Error(1)
}
and Then I tried to use the mock like this
mockSomethignRepository.On("FindById", "1").Return(nil, fmt.Errorf("something went wrong"))
I got similar error nterface conversion: interface {} is *[]entity.Session, not []entity.Session
I have found solution in https://go.dev/play/p/CLyY2y9-2VF
so I have solved it by
type MockSomethingRepository struct {
mock.Mock
}
func (msr *MockSomethingRepository) FindById(id string) (*entity.Session, error) {
args := msr.Called(id)
if args.Error(1) != nil {
return nil, args.Error(1)
}
return args.Get(0).(*entity.Session), args.Error(1)
}
I don't have an explanation but It works!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论