Skip to content
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

protobuf/types/known/timestamppb not recognized by gRPC module #1579

Open
iisharankov opened this issue Nov 23, 2023 · 9 comments
Open

protobuf/types/known/timestamppb not recognized by gRPC module #1579

iisharankov opened this issue Nov 23, 2023 · 9 comments

Comments

@iisharankov
Copy link

iisharankov commented Nov 23, 2023

What version of protobuf and what language are you using?

Version: google.golang.org/protobuf v1.31.0

What did you do?

I am trying to use a timestamppb type in my proto file, but even though the file exists timestamppb.go exists locally in the correct location, and I can import it in my own projects, my package (uploaded to a local artifactory storage solution then downloaded as a user) does not see the timestamppb module.

evaluation.proto
syntax = "proto3";

package evaluation.v1;

import "google/protobuf/timestamp.proto";

option go_package = "evaluation/v1";

[...]

message EvaluationContext {
  google.protobuf.Timestamp request_time = 1;       // Time of the request
}

The corresponding go.mod when running buf generate and the building in go looks like:

go.mod
module evaluation

go 1.20

require (
	google.golang.org/grpc v1.56.1
	google.golang.org/protobuf v1.31.0
)

require (
	github.com/golang/protobuf v1.5.3 // indirect
	golang.org/x/net v0.9.0 // indirect
	golang.org/x/sys v0.7.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
)

Above is all in the repository where the protobuf contracts are defined, built, and deployed to Artifactory.

Next, I have a project that downloads from Artifactory the contract and uses it. It also has a go.mod that looks like:

go.mod
module openfeature-provider-golang

go 1.21.3

require (
	github.com/open-feature/go-sdk v1.8.0
	google.golang.org/grpc v1.59.0
	google.golang.org/protobuf v1.31.0
	evaluation v1.0.0
)

require (
	github.com/go-logr/logr v1.3.0 // indirect
	github.com/golang/protobuf v1.5.3 // indirect
	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
	golang.org/x/net v0.18.0 // indirect
	golang.org/x/sys v0.14.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
)
And the `main.go` looks like
main.go
package main

import (
	"context"
	"fmt"
	"time"

	provider "subDirectory/provider"

	"github.com/open-feature/go-sdk/pkg/openfeature"
	"google.golang.org/protobuf/types/known/timestamppb" // Imported just as a test to see if it resolves. Here it does
)

func main() {
	ctx := context.Background()
	serverURL := "localhost:8080"

	myProvider:= provider.MyProvider(serverURL , ctx)
	_ = timestamppb.Now()  // Resolves and works.

	// [ CODE ]

}

This repo uses the contract to send flag data to a server. Above, I've imported timestamppb, and it builds fine and is able to use timestamppb.Now() without issue. The problem is the provider module provider "subDirectory/provider" uses the evaluation.pb.go file from the protobuf contract, which in and of itself cannot resolve timestamppb for some reason. The provider.go file looks as follows:

provider.go
package provider

import (
	"context"
	"log"

	v1 "evaluation/evaluation/v1"

	"github.com/open-feature/go-sdk/pkg/openfeature"
	"google.golang.org/grpc"
)
[...]

Here, v1 "evaluation/evaluation/v1" calls the generated proto files, where the following two lines exist at the start of evaluation.pb.go:

evaluation.pb.go
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"

protoimpl imports without issue, but timestamppb fails, stating

$ go build
# evaluation/evaluation/v1
../../../../../go/pkg/mod/evaluation@v1.0.0/evaluation/v1/evaluation.pb.go:12:14: could not import google.golang.org/protobuf/types/known/timestamppb (open : no such file or directory)

This is in the evaluation.pb.go file, located at ~go/pkg/mod/evaluation@v1.0.0/evaluation/v1/evaluation.pb.go. It fails to find timestamppb located at :~/go/pkg/mod/google.golang.org/protobuf@v1.31.0/types/known/timestamppb/, even though in my workspace directory where the project i (the above main.go, the exact same import, timestamppb "google.golang.org/protobuf/types/known/timestamppb" is used and correctly resolves the package.

I've lost days to trying to understand where the issue is. Why does my project see google.golang.org/protobuf/types/known/timestamppb, but the protobuf generated file does not see it the same path?

Anything else we should know about your project / environment?

Have two go envs that matter, since my project has set GOPROXY

` go env` in my project
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/MYUSER/.cache/go-build'
GOENV='/home/MYUSER/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/MYUSER/go/pkg/mod'
GONOPROXY=''
GONOSUMDB='REDACTED.com'
GOOS='linux'
GOPATH='/home/MYUSER/go'
GOPRIVATE=''
GOPROXY='https://USERNAME:PASSWORD@artifactory.REDACTED.ca/artifactory/api/go/featureflag-go-local'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.3'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/MYUSER/Workspace/github/PROJECT/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3981534992=/tmp/go-build -gno-record-gcc-switches'

Yes, I had to change the proxy to collect from the local artifactory solution. The go.env in the protobuf file location (~go/pkg/mod/evaluation@v1.0.0/evaluation/v1/evaluation.pb.go) on the other hand is:

`go env` at protobuf repo
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/MYUSER/.cache/go-build'
GOENV='/home/MYUSER/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/MYUSER/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/MYUSER/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.3'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/MYUSER/go/pkg/mod/google.golang.org/protobuf@v1.31.0/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1863516080=/tmp/go-build -gno-record-gcc-switches'
@puellanivis
Copy link
Collaborator

I’m confused a lot by these examples. You seem to be using relative imports (which I have not used at all since we started using go modules), but also you seem to have two separate go.mod files?

@iisharankov
Copy link
Author

I have a repository where I define the .proto contract, and generate it into Golang among other languages. When pushing these changes, Jenkins builds the golang repo and generates the go.mod you see. So the first block (the contract) and the first go.mod are in a repo where the generated proto files. This I thought was useful as it shows that google.golang.org/protobuf v1.31.0 is a direct dependency and github.com/golang/protobuf v1.5.3 // indirect as an indirect dependency.

The next go.mod file is for a repo that downloads the latest contract version from artifactory, and uses it to actually serialize and send the data. For debugging, I imported "google.golang.org/protobuf/types/known/timestamppb" to see if it is resolved, and it is. provider "subDirectory/provider" is a relative import because it's a package in my repository. It is this package (shown in the next code block, package provider), that imports the contract (line v1 "evaluation/evaluation/v1"), which is at go/pkg/mod/evaluation@v1.0.0/evaluation/v1/evaluation.pb.go. Here the import line timestamppb "google.golang.org/protobuf/types/known/timestamppb" exists, which was auto generated by protobuf, and triggers the error seen in the go build step, stating timestamppb was not resolved since the directory/file was not there, even though it is.

There was a bit of clean up to post this, as well as the error being so strange (lost a few days to it pulling hairs) that it's hard to explain. But put in a sentence: Why is the protobuf auto generated timestamppb "google.golang.org/protobuf/types/known/timestamppb" import failing as unresolved when importing the golang package the contract files are in, even though every other project can resolve timestamppb "google.golang.org/protobuf/types/known/timestamppb" safely. Stranger yet, protoimpl "google.golang.org/protobuf/runtime/protoimpl" is respoved, meaning runetime/protoimpl is discovered, but types/known/timestamppb is not in the protobuf package.

@puellanivis
Copy link
Collaborator

Is this the complete and total output of the compilation error:

$ go build
# evaluation/evaluation/v1
../../../../../go/pkg/mod/evaluation@v1.0.0/evaluation/v1/evaluation.pb.go:12:14: could not import google.golang.org/protobuf/types/known/timestamppb (open : no such file or directory)

This message oddly suggest something is wrong with your evaluation.pb.go imports and this has nothing to do with timestamppb itself, could you post the first 20-ish or so lines?

@iisharankov
Copy link
Author

I didn't catch anything odd with the import line there, but sure thing!

evaluation_grpc.pb.go
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc             (unknown)
// source: evaluation/v1/evaluation.proto

package v1

import (
	context "context"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
)

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
evaluation.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.31.0
// 	protoc        (unknown)
// source: evaluation/v1/evaluation.proto

package v1

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

@puellanivis
Copy link
Collaborator

And can you confirm that this was the full output of compilation error?

@iisharankov
Copy link
Author

iisharankov commented Nov 27, 2023

Yes. In my personal project running go build gives exactly

$ go build
# evaluation/evaluation/v1
../../../go/pkg/mod/evaluation@v1.0.0/evaluation/v1/evaluation.pb.go:12:14: could not import google.golang.org/protobuf/types/known/timestamppb (open : no such file or directory)

With no other errors

@puellanivis
Copy link
Collaborator

Hm… this is just so confusing. I’m wondering if it’s possible for you to create a minimal repro, which we would be able to check out and poke around with?

@iisharankov
Copy link
Author

Sorry for the long delay. Needed some approval to upload things and had to clean the repo, and since then got quite sick for a bit. That time of year. I also worried this was an internal proxy issue and was wasting your time, but I cannot explain how that could be part of it anymore after another two weeks of wrangling with this.

Here's a link to a cleaned up repo of the feature flag system I'm building. Below is a simplified proto file that creates the same issue

evaluation.proto
syntax = "proto3";

package evaluation.v1;

import "google/protobuf/timestamp.proto";

option go_package = "flag-management-system/evaluation/v1";

service FlagManagementSystemService {
  rpc BoolEvaluation(BoolEvaluationRequest) returns (BoolEvaluationResponse) {}
}


// // // // // Base Response // // // // // 

// Response status structure
message ResponseStatus {
  bool success = 1;
  ErrorMessage error = 2;
}


// Error message structure
message ErrorMessage {
  string code = 1;
  string message = 2;
}


// // // // // Base Flag Evaluation // // // // // 

// Extended evaluation request to include more types
message EvaluationRequest {
  string flag_path = 1;
  EvaluationContext context = 10; // Possible addition of evaluation context?
}


// Response structure for different types of feature flags
message EvaluationResponse {
  string flag_path = 1;
  ResponseStatus status = 5;
}




// // // // // Bool Flag Evaluation // // // // // 

// Boolean flags request
message BoolEvaluationRequest {
  uint32 batch_size = 1;
  repeated EvaluationRequest requests = 10;
}


// Single Boolean flag response
message BoolSingleFlagResponse {
  bool value = 1;
  EvaluationResponse metadata = 5;
}


// Boolean flags response
message BoolEvaluationResponse {
  uint32 response_size = 1;
  repeated BoolSingleFlagResponse results = 10;
}




// // // // // Context Evaluation  // // // // // 

// Provider Context 
message EvaluationContext {

  // Provider-specific information
  string provider_id = 1;
  string provider_has = 2;
  
  // Environmental and temporal data
  google.protobuf.Timestamp request_time = 3; 
  string request_time_zone = 4;
  string provider_language = 5;
  string provider_version = 6;
}

And the relevant part of the buf.gen.yaml

buf.gen.yaml
version: v1
plugins:
  # Go
  - plugin: buf.build/protocolbuffers/go
    out: gen/go
    opt:
      - paths=source_relative
  - plugin: buf.build/grpc/go
    out: gen/go
    opt:
      - paths=source_relative

As said before, when this is generated with buf generate, I then go build and deploy via Jenkins to Artifactory (not included) so it can be downloaded and sourced by users. The repo shared has a provider that should download the protobuf files from artifactory (note the modification to GONOSUMDB). For a while I've worried this is an issue on the internal proxy and artifactory and not with protobuf, but I've continued going in circles for days, and can't understand why runetime/protoimpl is discovered, but types/known/timestamppb is not, as mentioned before. Let me know if there's anything more I can provide.

@puellanivis
Copy link
Collaborator

I checked out your repo, but I get an issue with REDACTED.com/evaluation@v1.0.0: malformed module path "REDACTED.com/evaluation": invalid char 'R' in first path element"

Removing this line from the go.mod I get main.go:8:2: package openfeature-provider-golang/provider is not in std (/usr/local/go/src/openfeature-provider-golang/provider

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants