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

Clients overhaul #403

Merged
merged 13 commits into from
Dec 5, 2023
Merged

Clients overhaul #403

merged 13 commits into from
Dec 5, 2023

Conversation

brennanjl
Copy link
Collaborator

@brennanjl brennanjl commented Nov 30, 2023

This PR depends on this proto PR: kwilteam/proto#26

Apologies for the mega branch. I've been trying to get better about this, but sort've got swept up in the tight deadline and the interconnectedness of these problems.

There are still a few minor things I am working through (particularly with validator integration tests, CI workflows, as well as any other edge cases I run into as I finish the guide docs), but the rest of it is in place.

This PR includes:

  • Automatic documentation generation for kwil-cli and kwil-admin.
  • kwil-admin is now a cobra app
  • kwil-admin now supports remote signing for validator commands
  • kwil-admin now supports unix as well as grpc
  • standard utilities for displaying / silencing outputs in kwil-cli and kwil-admin
  • transport layer clients for txsvc, adminsvc, and the gateway are now distinctly separate, and can easily be implemented with grpc, http, json-rpc, etc. (@Yaiba I did not yet make a client for the new function service, which contains VerifySignature. It was previously in core/client, but this was not the right place for it. I assume the gateway relies on this?)
  • adminclient and gatewayclient packages, that handle the usecase specific nuances (transport layers, cookies, auth, etc) using composed clients and transport layers
  • auto generated REST API code for txsvc
  • several bug fixes for both grpc and http txsvc
  • a simplification of the gateway auth (in core/gatewayclient), that supports both automatic and manual auth
  • and other minor improvements.

@Yaiba
Copy link
Contributor

Yaiba commented Nov 30, 2023

For VerifySignature, yes gateway relies on this.

@brennanjl
Copy link
Collaborator Author

Ok, then I can go ahead and make a client this morning.

go.mod Outdated Show resolved Hide resolved

// convertErr will convert the error to a known type, if possible.
// It is expected that the error is from a gRPC call.
func ConvertGRPCErr(err error) error {
Copy link
Member

@jchappelow jchappelow Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to do similar for different reasons (txquery and broadcast), with handling in both http and grpc. I had found a bunch of breakage when client switched to http by default.

a31a9a5

Same general approach, and not conflicting in concept. Also wrapping our error with join, but also combining with our own structured type.

I'll adapt things if this PR goes in first (probably should), so no need to change anything. Just noting.

@jchappelow
Copy link
Member

We can fix kwil-admin setup testnet as follows:

@@ -348,10 +349,15 @@ func incrementPort(incoming string, amt int) (string, error) {
                return "", err
        }
 
+       if res.Scheme == "unix" { // no port to increment
+               return incoming, nil
+       }
+
        // Split the URL into two parts: host (and possibly scheme) and port
        host, portStr, err := net.SplitHostPort(res.Host)
        if err != nil {
                // Handle the case where there is no scheme (like "localhost:50051")
+               // NOTE: we could also detect this before SplitHostPort by inspecting res.Opaque
                if strings.Contains(err.Error(), "missing port in address") {
                        parts := strings.Split(incoming, ":")
                        if len(parts) != 2 {

with a test case like:

		{
			input:  "unix:///tmp/kwil_admin.sock",
			amount: 99,
			want:   "unix:///tmp/kwil_admin.sock",
		},

@brennanjl brennanjl marked this pull request as ready for review December 2, 2023 04:49
@brennanjl
Copy link
Collaborator Author

brennanjl commented Dec 4, 2023

Just a heads up on a modification I made tonight:

All flags now use hyphens, instead of underscores. Apparently, this is specified in the GNU conventions: https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html

I was hesitant to make this change, but my rationale was that, after this release, we likely will not be able to change these since they are so critical to running kwild. Changing them also required me to change how we loaded configs (Viper forces all names to be the same, and since ENV vars and TOMLs use underscores, it wasn't possible to have both). We could actually get rid of Viper now; we simply use it for converting ENVs, and we could use https://github.com/kelseyhightower/envconfig to get the same effect with nesting.

@jchappelow jchappelow added this to the v0.6.0 milestone Dec 4, 2023
@jchappelow
Copy link
Member

I got a panic running acceptance tests via task test:act:

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x9fcc77]

goroutine 1 [running]:
encoding/json.(*encodeState).marshal.func1()
	/home/jon/go121/src/encoding/json/encode.go:291 +0x6d
panic({0xd8f9a0?, 0x16bd040?})
	/home/jon/go121/src/runtime/panic.go:914 +0x21f
github.com/kwilteam/kwil-db/core/client.(*Records).ExportString(0x0)
	/home/jon/kwil/git/kwil-db/core/client/iter.go:60 +0x17
github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/database.(*respRelations).MarshalJSON(0xda51a0?)
	/home/jon/kwil/git/kwil-db/cmd/kwil-cli/cmds/database/message.go:73 +0x16
encoding/json.marshalerEncoder(0xc00031c280, {0xda51a0?, 0xc000320120?, 0xd3fd60?}, {0x10?, 0x1?})
	/home/jon/go121/src/encoding/json/encode.go:442 +0xd2
encoding/json.structEncoder.encode({{{0xc000308240, 0x2, 0x2}, 0xc0003065d0, 0xc000306600}}, 0xc00031c280, {0xdc7300?, 0xc000320120?, 0x7fb7c5161a68?}, {0x0, ...})
	/home/jon/go121/src/encoding/json/encode.go:706 +0x21e
encoding/json.ptrEncoder.encode({0xc0000061a0?}, 0xc00031c280, {0xd96660?, 0xc000320120?, 0xd96660?}, {0xa0?, 0x61?})
	/home/jon/go121/src/encoding/json/encode.go:878 +0x20f
encoding/json.(*encodeState).reflectValue(0xc0001f37b8?, {0xd96660?, 0xc000320120?, 0xc0001f3850?}, {0x20?, 0xb7?})
	/home/jon/go121/src/encoding/json/encode.go:323 +0x73
encoding/json.(*encodeState).marshal(0x419dc8?, {0xd96660?, 0xc000320120?}, {0x5e?, 0x0?})
	/home/jon/go121/src/encoding/json/encode.go:295 +0xb9
encoding/json.Marshal({0xd96660, 0xc000320120})
	/home/jon/go121/src/encoding/json/encode.go:162 +0xd0
encoding/json.MarshalIndent({0xd96660?, 0xc000320120?}, {0x0, 0x0}, {0xea7a3e, 0x2})
	/home/jon/go121/src/encoding/json/encode.go:175 +0x47
github.com/kwilteam/kwil-db/cmd/common/display.(*wrappedMsg).printJson(0x58024b?, {0xffd6c0, 0xc00011e020}, {0x6?, 0xea9b6d?})
	/home/jon/kwil/git/kwil-db/cmd/common/display/format.go:88 +0x3f
github.com/kwilteam/kwil-db/cmd/common/display.prettyPrint(0xc0001f3a40?, {0x7ffd025b51cc?, 0xffd780?}, {0xffd6c0?, 0xc00011e020?}, {0xffd6c0?, 0xc00011e028?})
	/home/jon/kwil/git/kwil-db/cmd/common/display/format.go:132 +0x6a
github.com/kwilteam/kwil-db/cmd/common/display.PrintCmd(0xeb9570?, {0x10013d8?, 0xc000326080})
	/home/jon/kwil/git/kwil-db/cmd/common/display/format.go:172 +0xfe
github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/database.callCmd.func1.1({0x10048b8, 0x1765020}, {0x100f908, 0xc000332000}, 0x14?)
	/home/jon/kwil/git/kwil-db/cmd/kwil-cli/cmds/database/call.go:88 +0x42e
github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/common.DialClient({0x10048b8, 0x1765020}, 0xc000252f00, 0x2, 0xc0001f3ca8)
	/home/jon/kwil/git/kwil-db/cmd/kwil-cli/cmds/common/roundtripper.go:111 +0x5df
github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/database.callCmd.func1(0xc000130c00?, {0xc000280000?, 0x4?, 0xea82bc?})
	/home/jon/kwil/git/kwil-db/cmd/kwil-cli/cmds/database/call.go:56 +0x8c
github.com/spf13/cobra.(*Command).execute(0xc000252f00, {0xc000259ee0, 0xd, 0xd})
	/home/jon/go/pkg/mod/github.com/spf13/cobra@v1.7.0/command.go:940 +0x87c
github.com/spf13/cobra.(*Command).ExecuteC(0x16d4460)
	/home/jon/go/pkg/mod/github.com/spf13/cobra@v1.7.0/command.go:1068 +0x3a5
github.com/spf13/cobra.(*Command).Execute(0x46cc1a?)
	/home/jon/go/pkg/mod/github.com/spf13/cobra@v1.7.0/command.go:992 +0x13
main.main()
	/home/jon/kwil/git/kwil-db/cmd/kwil-cli/main.go:13 +0x1d

@jchappelow
Copy link
Member

That appears to be from kwil-cli, spewed to stdout, not from the test framework, just to be clear.

Copy link
Member

@jchappelow jchappelow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a few waves of review I think. Here's some initial observations mostly from trying things out.

.github/workflows/ci.yaml Outdated Show resolved Hide resolved
Taskfile-pb.yml Outdated Show resolved Hide resolved
Taskfile-pb.yml Show resolved Hide resolved
build/package/docker/kwild.dockerfile Show resolved Hide resolved
@brennanjl
Copy link
Collaborator Author

That appears to be from kwil-cli, spewed to stdout, not from the test framework, just to be clear.

I can't understand why that would be happening... The tests pass fine on Github. Did you rebuild?

@jchappelow
Copy link
Member

That appears to be from kwil-cli, spewed to stdout, not from the test framework, just to be clear.

I can't understand why that would be happening... The tests pass fine on Github. Did you rebuild?

Yup. It's also happening in CI: https://github.com/kwilteam/kwil-db/actions/runs/7093407153/job/19306714183?pr=403#step:15:110

Comment on lines +193 to +195
// Merge merges b onto a, overwriting any fields in a that are also set in b.
func (a *KwildConfig) Merge(b *KwildConfig) error {
return merge.MergeWithOverwrite(a, b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this different from *a = *b? The docs for this merge package are confusing around the "empty" concept. What does empty mean for bool or int?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MergeWithOverwrite will merge take any non-zero values in b and set them in a.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty for bool or int means it is the zero value, and therefore does not merge: darccio/mergo#178

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not correct in all cases then. What if the override is to set something to zero or false?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I see. I guess we need some other way of checking that fields have been modified?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also go back to the old way, however I may need some help. I spent almost 2 hours trying to get viper to support underscore in toml/envs and hyphens in flags, and could not figure out why it was not working. That's why I ended up just doing this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I see. Ooof. Lemme see if there are any fields at present where this would be a problem in practice.

Changing some fields to pointers is also a soln, but not pretty.

Copy link
Collaborator Author

@brennanjl brennanjl Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppConfig.EnableRPCTLS, SnapshotConfig.Enabled, P2PConfig.AddrBookStrict, P2PConfig.MaxNumInboundPeers, P2PConfig.MaxNumOutboundPeers, P2PConfig.PexReactor, and P2PConfig.AllowDuplicateIP, and StateSyncConfig.Enable all seems like ones where a zero value could be sensible. Mempool also has some int values, but I don't think 0 values would make sense / be usable.

Copy link
Member

@jchappelow jchappelow Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only one there where the default is non-zero-valued (and thus might need to be overridden with the zero value) is AllowDuplicateIP:

AllowDuplicateIP: true, // override comet

I think we can punt on this, but seems like a real issue to solve at some point.

Comment on lines +308 to +333
// unmarshal toml to maps
var cfg map[string]interface{}
err = toml.Unmarshal(bts, &cfg)
if err != nil {
return nil, fmt.Errorf("failed to parse config file: %v", err)
}
return nil

// convert mapstructure toml to KwilConfig
var kwilCfg KwildConfig

mapDecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Copy link
Member

@jchappelow jchappelow Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably being dense, but I thought the idea of the pelletier/go-toml package was to unmarshal directly into your toml tagged struct. How come we have to go through the map[string]interface{} followed by mapstructure? I would expect to see the tags change to e.g. toml:"persistent_peers" and directly unmarshal into a *KwildConfig using toml.Unmarshal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was simply for compatibility with the existing struct tags. We still use viper for parsing the ENVs, so I just kept it

test/integration/helper.go Outdated Show resolved Hide resolved
test/integration/kwild_test.go Outdated Show resolved Hide resolved
build/package/docker/kwild.dockerfile Show resolved Hide resolved
test/integration/helper.go Show resolved Hide resolved
Comment on lines +132 to +140
func (c *GatewayClient) GetAuthCookie() (cookie *http.Cookie, found bool) {
cookies := c.httpClient.Jar.Cookies(c.target)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure using c.target here with Jar.Cookies() restricts kgw to setting the cookie path to /

@@ -265,15 +427,15 @@ func DefaultConfig() *KwildConfig {
},
ChainCfg: &ChainConfig{
P2P: &P2PConfig{
ListenAddress: "tcp://0.0.0.0:26656",
ListenAddress: "tcp://127.0.0.1:26656",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is why you had to update the docker-compose.yml to get blocks mining. I think this should say at 0.0.0.0 not a loopback. The p2p should be listening on all interfaces I believe.
Depending on where we go with #408, validators might want to be less promiscuous, but in our current model even the validators must be listening.

internal/abci/utils.go Outdated Show resolved Hide resolved
Copy link
Member

@jchappelow jchappelow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huge, but working well for me, including the gateway client bits.

Shall we wait for Gavin to circle back on this or get it merged?

Last step is rebase and task git:advance && git add proto ...

@brennanjl
Copy link
Collaborator Author

I'd be inclined to merge it, simply so that we can get everything else merged and ready for him. Easier to go back and do a last minute PR if something needs changed. He likely won't be working for 4 more hours.

@jchappelow
Copy link
Member

jchappelow commented Dec 5, 2023

Sounds good, let's rebase, update proto, and merge.

But actually, I hit a 401 on an authenticated call even after success on utils authenticate.

$  ./kwil-cli utils authenticate --kwil-provider http://127.0.0.1:8090
http://127.0.0.1:8090 wants you to sign in with your account:

I agree to the terms and conditions.

URI: http://127.0.0.1:8090/auth
Version: 1
Chain ID: kwil-chain-v8jwaldr
Nonce: d17db34847099c39c7c3
Issue At: 2023-12-05T21:07:21Z
Expiration Time: 2023-12-05T21:07:51Z

Do you want to sign this message?: y
Success

  ./kwil-cli database call -a get_post_authenticated -n testdb 'id:1' --kwil-provider http://127.0.0.1:8090
error calling action: call action: 401 Unauthorized
unauthorized

The ~/.kwil_cli/auth.json looks sus:

{
  "3Bj0mT6TtQSG4+VOJ9kdV87h2gc=": {
    "name": "kgw_session",
    "value": "OG8hhQpCkEiwM6XP0CyNpSCZTwSrjQqH8MVtCFFSuwE5ZjUyMDEwYjE1NjdlMjVhMzNjYQ==",
    "expires": "0001-01-01T00:00:00Z"
  }
}

That expires is surely incorrect?

Did I do something incorrect?

@brennanjl
Copy link
Collaborator Author

Did I do something incorrect?

Yeah, you have to pass the --authenticate flag. However, it does look like there is a bug with cookie persistence

@brennanjl
Copy link
Collaborator Author

Seems to not be an issue with cookie persistence. Seems to actually be an issue with cookie issuance

@jchappelow
Copy link
Member

Yeah SaveCookie is working fine.

brennanjl and others added 13 commits December 5, 2023 15:41
auto kwil cli docs

mostly finished with migrating kwil admin, still cleaning uo

mostly finished with migrating kwil admin, still cleaning up

refactored kwil admin into a standard cobra app

minor changes to doc generation

registering txsvc in the admin svc as well

made an example admin grpc client

added admin client that includes kwild client

added documentation for tls

added protobuf for expanded admin svc

various changes and cleanup for rpcs

http codegen

added http client using generated stubs, fixed several bugs in current proto definitions

refactoring the clients

gateway client concept

added gateway client to kwil-cli

everything building, still some issues

various bug fixes

final cleanup, tweaks

fixed bug causing failures in integration tests

 update ci check swagger generated code

added url parsing util since stdlib is very finnicky when managing grpc, http, and unix

update linter skip dirs

update assert import

tidy

enable swagger check

added integration test mounting for volumes

fixed docker compose issue in acceptance test

debugging integration test unix sockets

improved output formatting for kwil-admin

various upgrades to tests for validators

fixed all integration tests

updated run permissions of swagger check script

addressed Jons feedback, trying new permission with failing script

fixed failing unit tests

added kwil-admin to main dockerfile

retrying due to transient ci error

trying to fix failing CI build

fix dockerfile copy command

added a node info utility, and also added integration tests

changed all kwild flags to use hyphens, since it is far more common

figured bug caused by updating flags to hyphens from underscores

changed flags to use hyphens instead of underscores in flags, to be in line with GNU standards

fixed typo that linter was complaining about

added functionsvc client

removed node info, due to security issues

added setup peer command, which makes it easy to join a running network. Also changed genesis to exist in root dir

added list pending joins

output and formatting upgrades
…needed for kwild, and renamed client constructors
@jchappelow jchappelow merged commit 387a661 into main Dec 5, 2023
2 checks passed
@jchappelow jchappelow deleted the clients-overhaul branch December 5, 2023 21:50
@Yaiba
Copy link
Contributor

Yaiba commented Dec 6, 2023

That expires is surely incorrect?

I think it's the default cookiejar issue,

func Test_cookiejar(t *testing.T) {
	exp := time.Now().Add(time.Hour * 720)
	rawExp := exp.UTC().Format(time.RFC1123)
	cookie := http.Cookie{
		Name:       "test",
		Value:      "test",
		Path:       "/",
		Domain:     "a.com",
		Expires:    exp,
		RawExpires: rawExp,
	}

	fmt.Printf("cookies: %v\n", cookie.Expires)

	jar, _ := cookiejar.New(nil)

	u, _ := url.Parse("http://a.com")

	jar.SetCookies(u, []*http.Cookie{&cookie})

	ck := jar.Cookies(u)[0]
	fmt.Printf("cookies in the jar: %v\n", ck.Expires)
}

And the outout:

=== RUN   Test_cookiejar
cookies: 2024-01-05 17:35:25.370542 +0800 CST m=+2592000.018042334
cookies in the jar: 0001-01-01 00:00:00 +0000 UTC

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

Successfully merging this pull request may close these issues.

3 participants