[MM-27535] User invite limits for MM Cloud (#15197)

* Add a config for MM User Limit

* Adding graceful errors for if an administrator invites people passed their user limit

* Including changed vendor files

* Adding unit test

* Fix a bug

* Push up working tests (Thanks Joram)

* Add more cases, clean up logs in code

* One more case

* Refactoring based on PR comments

* Updating i18n

* Some changes based on PR review

* Remove a comment

* Bring back some translations that were somehow removed

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
nickmisasi
2020-08-19 16:01:07 -04:00
committed by GitHub
parent 4e154756bd
commit 769f39eee5
20 changed files with 452 additions and 156 deletions

View File

@@ -1177,6 +1177,7 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
}
emailList := model.ArrayFromJson(r.Body)
for i := range emailList {
emailList[i] = strings.ToLower(emailList[i])
}
@@ -1193,7 +1194,21 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("emails", emailList)
if graceful {
invitesWithError, err := c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId)
cloudUserLimit := *c.App.Config().ExperimentalSettings.CloudUserLimit
var invitesOverLimit []*model.EmailInviteWithError
if cloudUserLimit > 0 && c.IsSystemAdmin() {
emailList, invitesOverLimit, _ = c.App.GetErrorListForEmailsOverLimit(emailList, cloudUserLimit)
}
var invitesWithError []*model.EmailInviteWithError
var err *model.AppError
if emailList != nil {
invitesWithError, err = c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId)
}
if len(invitesOverLimit) > 0 {
invitesWithError = append(invitesWithError, invitesOverLimit...)
}
if invitesWithError != nil {
errList := make([]string, 0, len(invitesWithError))
for _, inv := range invitesWithError {

View File

@@ -2721,6 +2721,69 @@ func TestImportTeam(t *testing.T) {
})
}
func TestInviteUsersToTeamWithUserLimit(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
email1 := th.GenerateTestEmail()
email2 := th.GenerateTestEmail()
email3 := th.GenerateTestEmail()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableEmailInvitations = true
*cfg.ExperimentalSettings.CloudUserLimit = 2
})
t.Run("System admin, invite when at limit should fail", func(t *testing.T) {
invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
CheckNoError(t, resp)
require.Len(t, invitesWithErrors, 2)
require.NotNil(t, invitesWithErrors[0].Error)
assert.Equal(t, invitesWithErrors[0].Error.Message, "You've reached the user limit of your current tier")
require.NotNil(t, invitesWithErrors[1].Error)
assert.Equal(t, invitesWithErrors[1].Error.Message, "You've reached the user limit of your current tier")
})
t.Run("Regular user, invite when at limit should succeed", func(t *testing.T) {
invitesWithErrors, resp := th.Client.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email3})
CheckNoError(t, resp)
require.Len(t, invitesWithErrors, 1)
assert.Nil(t, invitesWithErrors[0].Error)
})
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ExperimentalSettings.CloudUserLimit = 5
})
t.Run("With one remaining user inviting more than one user as admin invites only one user", func(t *testing.T) {
invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
CheckNoError(t, resp)
require.Len(t, invitesWithErrors, 2)
require.Nil(t, invitesWithErrors[0].Error)
require.NotNil(t, invitesWithErrors[1].Error)
assert.Equal(t, invitesWithErrors[1].Error.Message, "You've reached the user limit of your current tier")
})
t.Run("With one remaining user inviting more than one user as a regular user sends all invites", func(t *testing.T) {
invitesWithErrors, resp := th.Client.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
CheckNoError(t, resp)
require.Len(t, invitesWithErrors, 2)
assert.Nil(t, invitesWithErrors[0].Error)
assert.Nil(t, invitesWithErrors[1].Error)
})
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ExperimentalSettings.CloudUserLimit = 100
})
t.Run("Invited user count is well below limit", func(t *testing.T) {
invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
CheckNoError(t, resp)
require.Len(t, invitesWithErrors, 2)
require.Nil(t, invitesWithErrors[0].Error)
})
}
func TestInviteUsersToTeam(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

View File

@@ -537,6 +537,7 @@ type AppIface interface {
GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError)
GetEmojiImage(emojiId string) ([]byte, string, *model.AppError)
GetEmojiList(page, perPage int, sort string) ([]*model.Emoji, *model.AppError)
GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError)
GetFile(fileId string) ([]byte, *model.AppError)
GetFileInfo(fileId string) (*model.FileInfo, *model.AppError)
GetFileInfos(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, *model.AppError)

View File

@@ -5069,6 +5069,28 @@ func (a *OpenTracingAppLayer) GetEnvironmentConfig() map[string]interface{} {
return resultVar0
}
func (a *OpenTracingAppLayer) GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetErrorListForEmailsOverLimit")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1, resultVar2 := a.app.GetErrorListForEmailsOverLimit(emailList, cloudUserLimit)
if resultVar2 != nil {
span.LogFields(spanlog.Error(resultVar2))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1, resultVar2
}
func (a *OpenTracingAppLayer) GetFile(fileId string) ([]byte, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFile")

View File

@@ -1127,6 +1127,47 @@ func (a *App) prepareInviteNewUsersToTeam(teamId, senderId string) (*model.User,
return user, team, nil
}
func genEmailInviteWithErrorList(emailList []string) []*model.EmailInviteWithError {
invitesNotSent := make([]*model.EmailInviteWithError, len(emailList))
for i := range emailList {
invite := &model.EmailInviteWithError{
Email: emailList[i],
Error: model.NewAppError("inviteUsersToTeam", "api.team.invite_members.limit_reached.app_error", map[string]interface{}{"Addresses": emailList[i]}, "", http.StatusBadRequest),
}
invitesNotSent[i] = invite
}
return invitesNotSent
}
func (a *App) GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) {
var invitesNotSent []*model.EmailInviteWithError
if cloudUserLimit <= 0 {
return emailList, invitesNotSent, nil
}
systemUserCount, _ := a.Srv().Store.User().Count(model.UserCountOptions{})
remainingUsers := cloudUserLimit - systemUserCount
if remainingUsers <= 0 {
// No remaining users so all fail
invitesNotSent = genEmailInviteWithErrorList(emailList)
emailList = nil
} else if remainingUsers < int64(len(emailList)) {
// Trim the email list to only invite as many users as are remaining in subscription
// Set graceful errors for the remaining email addresses
emailsAboveLimit := emailList[remainingUsers:]
invitesNotSent = genEmailInviteWithErrorList(emailsAboveLimit)
// If 1 user remaining we have to prevent 0:0 reslicing
if remainingUsers == 1 {
email := emailList[0]
emailList = nil
emailList = append(emailList, email)
} else {
emailList = emailList[:(remainingUsers - 1)]
}
}
return emailList, invitesNotSent, nil
}
func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderId string) ([]*model.EmailInviteWithError, *model.AppError) {
if !*a.Config().ServiceSettings.EnableEmailInvitations {
return nil, model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.disabled.app_error", nil, "", http.StatusNotImplemented)
@@ -1136,7 +1177,6 @@ func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderI
err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.no_one.app_error", nil, "", http.StatusBadRequest)
return nil, err
}
user, team, err := a.prepareInviteNewUsersToTeam(teamId, senderId)
if err != nil {
return nil, err

View File

@@ -52,6 +52,7 @@ func GenerateClientConfig(c *model.Config, diagnosticID string, license *model.L
props["ExperimentalEnablePostMetadata"] = "true"
props["ExperimentalEnableClickToReply"] = strconv.FormatBool(*c.ExperimentalSettings.EnableClickToReply)
props["ExperimentalCloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 8)
if *c.ServiceSettings.ExperimentalChannelOrganization || *c.ServiceSettings.ExperimentalGroupUnreadChannels != model.GROUP_UNREAD_CHANNELS_DISABLED {
props["ExperimentalChannelOrganization"] = strconv.FormatBool(true)
} else {
@@ -96,6 +97,8 @@ func GenerateClientConfig(c *model.Config, diagnosticID string, license *model.L
props["EnableEmailInvitations"] = strconv.FormatBool(*c.ServiceSettings.EnableEmailInvitations)
props["CloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 10)
// Set default values for all options that require a license.
props["ExperimentalHideTownSquareinLHS"] = "false"
props["ExperimentalTownSquareIsReadOnly"] = "false"

8
go.mod
View File

@@ -85,7 +85,7 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect
github.com/rs/cors v1.7.0
github.com/rudderlabs/analytics-go v3.2.1+incompatible
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
github.com/sirupsen/logrus v1.6.0
@@ -108,6 +108,9 @@ require (
github.com/wiggin77/merror v1.0.2
github.com/wiggin77/srslog v1.0.1
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
github.com/ziutek/mymysql v1.5.4 // indirect
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a // indirect
go.etcd.io/bbolt v1.3.5 // indirect
go.uber.org/zap v1.15.0
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/image v0.0.0-20200618115811-c13761719519
@@ -116,7 +119,8 @@ require (
golang.org/x/net v0.0.0-20200707034311-ab3426394381
golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd // indirect
golang.org/x/text v0.3.3
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5 // indirect
google.golang.org/grpc v1.30.0 // indirect

9
go.sum
View File

@@ -760,8 +760,11 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a h1:00UFliGZl2UciXe8o/2iuEsRQ9u7z0rzDTVzuj6EYY0=
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a/go.mod h1:ofmGw6LrMypycsiWcyug6516EXpIxSbZ+uI9ppGypfY=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@@ -866,6 +869,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -919,6 +923,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181207195948-8634b1ecd393/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -939,10 +944,14 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f h1:JcoF/bowzCDI+MXu1yLqQGNO3ibqWsWq+Sk7pOT218w=
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692 h1:fsn47thVa7Ar/TMyXYlZgOoT7M4+kRpb+KpSAqRQx1w=
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=

View File

@@ -4,8 +4,8 @@ go 1.14
require (
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 // indirect
github.com/philhofer/fwd v1.0.0 // indirect
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292 // indirect
github.com/philhofer/fwd v1.0.0 // indirect
github.com/reflog/struct2interface v0.6.1 // indirect
github.com/tinylib/msgp v1.1.2 // indirect
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect

View File

@@ -58,6 +58,8 @@ github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200721093743-053c38
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200721093743-053c38dcd293/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM=
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 h1:+JXFDXEkbMieBt54Rihj3ppnZ/Pm1kZxkLOD0hrWFzs=
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM=
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292 h1:swpUQyq7HWhc0aStUAgdZI6o0UOD9OzXJxbxUTLo1fo=
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=

View File

@@ -2214,6 +2214,10 @@
"id": "api.team.invite_members.invalid_email.app_error",
"translation": "The following email addresses do not belong to an accepted domain: {{.Addresses}}. Please contact your System Administrator for details."
},
{
"id": "api.team.invite_members.limit_reached.app_error",
"translation": "You've reached the user limit of your current tier"
},
{
"id": "api.team.invite_members.no_one.app_error",
"translation": "No one to invite."

View File

@@ -861,6 +861,7 @@ type ExperimentalSettings struct {
EnableClickToReply *bool `restricted:"true"`
LinkMetadataTimeoutMilliseconds *int64 `restricted:"true"`
RestrictSystemAdmin *bool `restricted:"true"`
CloudUserLimit *int64 `restricted:"true"`
UseNewSAMLLibrary *bool
}
@@ -884,6 +885,11 @@ func (s *ExperimentalSettings) SetDefaults() {
if s.RestrictSystemAdmin == nil {
s.RestrictSystemAdmin = NewBool(false)
}
if s.CloudUserLimit == nil {
// User limit 0 is treated as no limit
s.CloudUserLimit = NewInt64(0)
}
if s.UseNewSAMLLibrary == nil {
s.UseNewSAMLLibrary = NewBool(false)
}

View File

@@ -89,6 +89,10 @@ type golistState struct {
rootDirsError error
rootDirs map[string]string
goVersionOnce sync.Once
goVersionError error
goVersion string // third field of 'go version'
// vendorDirs caches the (non)existence of vendor directories.
vendorDirs map[string]bool
}
@@ -635,6 +639,39 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
pkg.CompiledGoFiles = pkg.GoFiles
}
// Temporary work-around for golang/go#39986. Parse filenames out of
// error messages. This happens if there are unrecoverable syntax
// errors in the source, so we can't match on a specific error message.
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
addFilenameFromPos := func(pos string) bool {
split := strings.Split(pos, ":")
if len(split) < 1 {
return false
}
filename := strings.TrimSpace(split[0])
if filename == "" {
return false
}
if !filepath.IsAbs(filename) {
filename = filepath.Join(state.cfg.Dir, filename)
}
info, _ := os.Stat(filename)
if info == nil {
return false
}
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
pkg.GoFiles = append(pkg.GoFiles, filename)
return true
}
found := addFilenameFromPos(err.Pos)
// In some cases, go list only reports the error position in the
// error text, not the error position. One such case is when the
// file's package name is a keyword (see golang.org/issue/39763).
if !found {
addFilenameFromPos(err.Err)
}
}
if p.Error != nil {
msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
// Address golang.org/issue/35964 by appending import stack to error message.
@@ -664,6 +701,58 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
return &response, nil
}
func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
return false
}
goV, err := state.getGoVersion()
if err != nil {
return false
}
// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
// The import stack behaves differently for these versions than newer Go versions.
if strings.HasPrefix(goV, "go1.13") || strings.HasPrefix(goV, "go1.14") {
return len(p.Error.ImportStack) == 0
}
// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
// or the current package is at the top of the import stack. This is not guaranteed
// to work perfectly, but should avoid some cases where files in errors don't belong to this
// package.
return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
}
func (state *golistState) getGoVersion() (string, error) {
state.goVersionOnce.Do(func() {
var b *bytes.Buffer
// Invoke go version. Don't use invokeGo because it will supply build flags, and
// go version doesn't expect build flags.
inv := gocommand.Invocation{
Verb: "version",
Env: state.cfg.Env,
Logf: state.cfg.Logf,
}
gocmdRunner := state.cfg.gocmdRunner
if gocmdRunner == nil {
gocmdRunner = &gocommand.Runner{}
}
b, _, _, state.goVersionError = gocmdRunner.RunRaw(state.cfg.Context, inv)
if state.goVersionError != nil {
return
}
sp := strings.Split(b.String(), " ")
if len(sp) < 3 {
state.goVersionError = fmt.Errorf("go version output: expected 'go version <version>', got '%s'", b.String())
return
}
state.goVersion = sp[2]
})
return state.goVersion, state.goVersionError
}
// getPkgPath finds the package path of a directory if it's relative to a root directory.
func (state *golistState) getPkgPath(dir string) (string, bool, error) {
absDir, err := filepath.Abs(dir)

View File

@@ -102,8 +102,11 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
}
}
}
// The overlay could have included an entirely new package.
if pkg == nil {
// The overlay could have included an entirely new package or an
// ad-hoc package. An ad-hoc package is one that we have manually
// constructed from inadequate `go list` results for a file= query.
// It will have the ID command-line-arguments.
if pkg == nil || pkg.ID == "command-line-arguments" {
// Try to find the module or gopath dir the file is contained in.
// Then for modules, add the module opath to the beginning.
pkgPath, ok, err := state.getPkgPath(dir)
@@ -127,35 +130,40 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
}
}
// Try to reclaim a package with the same ID, if it exists in the response.
for _, p := range response.dr.Packages {
if reclaimPackage(p, id, opath, contents) {
pkg = p
break
}
}
// Otherwise, create a new package.
if pkg == nil {
pkg = &Package{
PkgPath: pkgPath,
ID: id,
Name: pkgName,
Imports: make(map[string]*Package),
}
response.addPackage(pkg)
havePkgs[pkg.PkgPath] = id
// Add the production package's sources for a test variant.
if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
// Add the package under test and its imports to the test variant.
pkg.forTest = testVariantOf.PkgPath
for k, v := range testVariantOf.Imports {
pkg.Imports[k] = &Package{ID: v.ID}
if pkg != nil {
// TODO(rstambler): We should change the package's path and ID
// here. The only issue is that this messes with the roots.
} else {
// Try to reclaim a package with the same ID, if it exists in the response.
for _, p := range response.dr.Packages {
if reclaimPackage(p, id, opath, contents) {
pkg = p
break
}
}
if isXTest {
pkg.forTest = forTest
// Otherwise, create a new package.
if pkg == nil {
pkg = &Package{
PkgPath: pkgPath,
ID: id,
Name: pkgName,
Imports: make(map[string]*Package),
}
response.addPackage(pkg)
havePkgs[pkg.PkgPath] = id
// Add the production package's sources for a test variant.
if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
// Add the package under test and its imports to the test variant.
pkg.forTest = testVariantOf.PkgPath
for k, v := range testVariantOf.Imports {
pkg.Imports[k] = &Package{ID: v.ID}
}
}
if isXTest {
pkg.forTest = forTest
}
}
}
}

View File

@@ -3,8 +3,10 @@
package imports // import "golang.org/x/tools/imports"
import (
"io/ioutil"
"log"
"golang.org/x/tools/internal/gocommand"
intimp "golang.org/x/tools/internal/imports"
)
@@ -29,25 +31,34 @@ var Debug = false
var LocalPrefix string
// Process formats and adjusts imports for the provided file.
// If opt is nil the defaults are used.
// If opt is nil the defaults are used, and if src is nil the source
// is read from the filesystem.
//
// Note that filename's directory influences which imports can be chosen,
// so it is important that filename be accurate.
// To process data ``as if'' it were in filename, pass the data as a non-nil src.
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
var err error
if src == nil {
src, err = ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
}
if opt == nil {
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
}
intopt := &intimp.Options{
Env: &intimp.ProcessEnv{
LocalPrefix: LocalPrefix,
GocmdRunner: &gocommand.Runner{},
},
AllErrors: opt.AllErrors,
Comments: opt.Comments,
FormatOnly: opt.FormatOnly,
Fragment: opt.Fragment,
TabIndent: opt.TabIndent,
TabWidth: opt.TabWidth,
LocalPrefix: LocalPrefix,
AllErrors: opt.AllErrors,
Comments: opt.Comments,
FormatOnly: opt.FormatOnly,
Fragment: opt.Fragment,
TabIndent: opt.TabIndent,
TabWidth: opt.TabWidth,
}
if Debug {
intopt.Env.Logf = log.Printf

View File

@@ -32,25 +32,25 @@ import (
// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool){
func(env *ProcessEnv, importPath string) (num int, ok bool) {
if env.LocalPrefix == "" {
var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){
func(localPrefix, importPath string) (num int, ok bool) {
if localPrefix == "" {
return
}
for _, p := range strings.Split(env.LocalPrefix, ",") {
for _, p := range strings.Split(localPrefix, ",") {
if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
return 3, true
}
}
return
},
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
func(_, importPath string) (num int, ok bool) {
if strings.HasPrefix(importPath, "appengine") {
return 2, true
}
return
},
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
func(_, importPath string) (num int, ok bool) {
firstComponent := strings.Split(importPath, "/")[0]
if strings.Contains(firstComponent, ".") {
return 1, true
@@ -59,9 +59,9 @@ var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool
},
}
func importGroup(env *ProcessEnv, importPath string) int {
func importGroup(localPrefix, importPath string) int {
for _, fn := range importToGroup {
if n, ok := fn(env, importPath); ok {
if n, ok := fn(localPrefix, importPath); ok {
return n
}
}
@@ -278,7 +278,12 @@ func (p *pass) loadPackageNames(imports []*ImportInfo) error {
unknown = append(unknown, imp.ImportPath)
}
names, err := p.env.GetResolver().loadPackageNames(unknown, p.srcDir)
resolver, err := p.env.GetResolver()
if err != nil {
return err
}
names, err := resolver.loadPackageNames(unknown, p.srcDir)
if err != nil {
return err
}
@@ -640,15 +645,23 @@ func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filena
wrappedCallback.exportsLoaded(pkg, exports)
},
}
return env.GetResolver().scan(ctx, scanFilter)
resolver, err := env.GetResolver()
if err != nil {
return err
}
return resolver.scan(ctx, scanFilter)
}
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) map[string]int {
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]int, error) {
result := make(map[string]int)
for _, path := range paths {
result[path] = env.GetResolver().scoreImportPath(ctx, path)
resolver, err := env.GetResolver()
if err != nil {
return nil, err
}
return result
for _, path := range paths {
result[path] = resolver.scoreImportPath(ctx, path)
}
return result, nil
}
func PrimeCache(ctx context.Context, env *ProcessEnv) error {
@@ -674,8 +687,9 @@ func candidateImportName(pkg *pkg) string {
return ""
}
// getAllCandidates gets all of the candidates to be imported, regardless of if they are needed.
func getAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
// GetAllCandidates gets all of the packages starting with prefix that can be
// imported by filename, sorted by import path.
func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
callback := &scanCallback{
rootFound: func(gopathwalk.Root) bool {
return true
@@ -714,7 +728,8 @@ type PackageExport struct {
Exports []string
}
func getPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
// GetPackageExports returns all known packages with name pkg and their exports.
func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
callback := &scanCallback{
rootFound: func(gopathwalk.Root) bool {
return true
@@ -749,8 +764,6 @@ var RequiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD"
// ProcessEnv contains environment variables and settings that affect the use of
// the go command, the go/build package, etc.
type ProcessEnv struct {
LocalPrefix string
GocmdRunner *gocommand.Runner
BuildFlags []string
@@ -830,16 +843,19 @@ func (e *ProcessEnv) env() []string {
return env
}
func (e *ProcessEnv) GetResolver() Resolver {
func (e *ProcessEnv) GetResolver() (Resolver, error) {
if e.resolver != nil {
return e.resolver
return e.resolver, nil
}
if err := e.init(); err != nil {
return nil, err
}
if len(e.Env["GOMOD"]) == 0 {
e.resolver = newGopathResolver(e)
return e.resolver
return e.resolver, nil
}
e.resolver = newModuleResolver(e)
return e.resolver
return e.resolver, nil
}
func (e *ProcessEnv) buildContext() *build.Context {
@@ -964,10 +980,13 @@ func addExternalCandidates(pass *pass, refs references, filename string) error {
return false // We'll do our own loading after we sort.
},
}
err := pass.env.GetResolver().scan(context.Background(), callback)
resolver, err := pass.env.GetResolver()
if err != nil {
return err
}
if err = resolver.scan(context.Background(), callback); err != nil {
return err
}
// Search for imports matching potential package references.
type result struct {
@@ -1408,6 +1427,10 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
}
}
resolver, err := pass.env.GetResolver()
if err != nil {
return nil, err
}
// Collect exports for packages with matching names.
rescv := make([]chan *pkg, len(candidates))
@@ -1446,7 +1469,7 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
}
// If we're an x_test, load the package under test's test variant.
includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
_, exports, err := pass.env.GetResolver().loadExports(ctx, c.pkg, includeTest)
_, exports, err := resolver.loadExports(ctx, c.pkg, includeTest)
if err != nil {
if pass.env.Logf != nil {
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)

View File

@@ -11,7 +11,6 @@ package imports
import (
"bufio"
"bytes"
"context"
"fmt"
"go/ast"
"go/format"
@@ -19,19 +18,22 @@ import (
"go/printer"
"go/token"
"io"
"io/ioutil"
"regexp"
"strconv"
"strings"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/gocommand"
)
// Options is golang.org/x/tools/imports.Options with extra internal-only options.
type Options struct {
Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state.
// LocalPrefix is a comma-separated string of import path prefixes, which, if
// set, instructs Process to sort the import paths with the given prefixes
// into another group after 3rd-party packages.
LocalPrefix string
Fragment bool // Accept fragment of a source file (no package statement)
AllErrors bool // Report all errors (not just the first 10 on different lines)
@@ -42,13 +44,8 @@ type Options struct {
FormatOnly bool // Disable the insertion and deletion of imports
}
// Process implements golang.org/x/tools/imports.Process with explicit context in env.
// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env.
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
src, opt, err = initialize(filename, src, opt)
if err != nil {
return nil, err
}
fileSet := token.NewFileSet()
file, adjust, err := parse(fileSet, filename, src, opt)
if err != nil {
@@ -64,16 +61,12 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
}
// FixImports returns a list of fixes to the imports that, when applied,
// will leave the imports in the same state as Process.
// will leave the imports in the same state as Process. src and opt must
// be specified.
//
// Note that filename's directory influences which imports can be chosen,
// so it is important that filename be accurate.
func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
src, opt, err = initialize(filename, src, opt)
if err != nil {
return nil, err
}
fileSet := token.NewFileSet()
file, _, err := parse(fileSet, filename, src, opt)
if err != nil {
@@ -84,13 +77,9 @@ func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix,
}
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
// is added in when parsing the file.
// is added in when parsing the file. src and opts must be specified, but no
// env is needed.
func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) {
src, opt, err = initialize(filename, src, opt)
if err != nil {
return nil, err
}
// Don't use parse() -- we don't care about fragments or statement lists
// here, and we need to work with unparseable files.
fileSet := token.NewFileSet()
@@ -114,60 +103,9 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e
return formatFile(fileSet, file, src, nil, opt)
}
// GetAllCandidates gets all of the packages starting with prefix that can be
// imported by filename, sorted by import path.
func GetAllCandidates(ctx context.Context, callback func(ImportFix), searchPrefix, filename, filePkg string, opt *Options) error {
_, opt, err := initialize(filename, []byte{}, opt)
if err != nil {
return err
}
return getAllCandidates(ctx, callback, searchPrefix, filename, filePkg, opt.Env)
}
// GetPackageExports returns all known packages with name pkg and their exports.
func GetPackageExports(ctx context.Context, callback func(PackageExport), searchPkg, filename, filePkg string, opt *Options) error {
_, opt, err := initialize(filename, []byte{}, opt)
if err != nil {
return err
}
return getPackageExports(ctx, callback, searchPkg, filename, filePkg, opt.Env)
}
// initialize sets the values for opt and src.
// If they are provided, they are not changed. Otherwise opt is set to the
// default values and src is read from the file system.
func initialize(filename string, src []byte, opt *Options) ([]byte, *Options, error) {
// Use defaults if opt is nil.
if opt == nil {
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
}
// Set the env if the user has not provided it.
if opt.Env == nil {
opt.Env = &ProcessEnv{}
}
// Set the gocmdRunner if the user has not provided it.
if opt.Env.GocmdRunner == nil {
opt.Env.GocmdRunner = &gocommand.Runner{}
}
if err := opt.Env.init(); err != nil {
return nil, nil, err
}
if src == nil {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, nil, err
}
src = b
}
return src, opt, nil
}
func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) {
mergeImports(opt.Env, fileSet, file)
sortImports(opt.Env, fileSet, file)
mergeImports(fileSet, file)
sortImports(opt.LocalPrefix, fileSet, file)
imps := astutil.Imports(fileSet, file)
var spacesBefore []string // import paths we need spaces before
for _, impSection := range imps {
@@ -178,7 +116,7 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(
lastGroup := -1
for _, importSpec := range impSection {
importPath, _ := strconv.Unquote(importSpec.Path.Value)
groupNum := importGroup(opt.Env, importPath)
groupNum := importGroup(opt.LocalPrefix, importPath)
if groupNum != lastGroup && lastGroup != -1 {
spacesBefore = append(spacesBefore, importPath)
}

View File

@@ -15,7 +15,7 @@ import (
// sortImports sorts runs of consecutive import lines in import blocks in f.
// It also removes duplicate imports when it is possible to do so without data loss.
func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
func sortImports(localPrefix string, fset *token.FileSet, f *ast.File) {
for i, d := range f.Decls {
d, ok := d.(*ast.GenDecl)
if !ok || d.Tok != token.IMPORT {
@@ -40,11 +40,11 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
for j, s := range d.Specs {
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
// j begins a new run. End this one.
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:j])...)
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:j])...)
i = j
}
}
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:])...)
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:])...)
d.Specs = specs
// Deduping can leave a blank line before the rparen; clean that up.
@@ -60,7 +60,7 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
// mergeImports merges all the import declarations into the first one.
// Taken from golang.org/x/tools/ast/astutil.
func mergeImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
func mergeImports(fset *token.FileSet, f *ast.File) {
if len(f.Decls) <= 1 {
return
}
@@ -142,7 +142,7 @@ type posSpan struct {
End token.Pos
}
func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
func sortSpecs(localPrefix string, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
// Can't short-circuit here even if specs are already sorted,
// since they might yet need deduplication.
// A lone import, however, may be safely ignored.
@@ -191,7 +191,7 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp
// Reassign the import paths to have the same position sequence.
// Reassign each comment to abut the end of its spec.
// Sort the comments by new position.
sort.Sort(byImportSpec{env, specs})
sort.Sort(byImportSpec{localPrefix, specs})
// Dedup. Thanks to our sorting, we can just consider
// adjacent pairs of imports.
@@ -245,8 +245,8 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp
}
type byImportSpec struct {
env *ProcessEnv
specs []ast.Spec // slice of *ast.ImportSpec
localPrefix string
specs []ast.Spec // slice of *ast.ImportSpec
}
func (x byImportSpec) Len() int { return len(x.specs) }
@@ -255,8 +255,8 @@ func (x byImportSpec) Less(i, j int) bool {
ipath := importPath(x.specs[i])
jpath := importPath(x.specs[j])
igroup := importGroup(x.env, ipath)
jgroup := importGroup(x.env, jpath)
igroup := importGroup(x.localPrefix, ipath)
jgroup := importGroup(x.localPrefix, jpath)
if igroup != jgroup {
return igroup < jgroup
}

View File

@@ -56,6 +56,7 @@ var stdlib = map[string][]string{
},
"bufio": []string{
"ErrAdvanceTooFar",
"ErrBadReadCount",
"ErrBufferFull",
"ErrFinalToken",
"ErrInvalidUnreadByte",
@@ -303,7 +304,9 @@ var stdlib = map[string][]string{
"PrivateKey",
"PublicKey",
"Sign",
"SignASN1",
"Verify",
"VerifyASN1",
},
"crypto/ed25519": []string{
"GenerateKey",
@@ -322,11 +325,13 @@ var stdlib = map[string][]string{
"CurveParams",
"GenerateKey",
"Marshal",
"MarshalCompressed",
"P224",
"P256",
"P384",
"P521",
"Unmarshal",
"UnmarshalCompressed",
},
"crypto/hmac": []string{
"Equal",
@@ -432,6 +437,7 @@ var stdlib = map[string][]string{
"CurveP521",
"Dial",
"DialWithDialer",
"Dialer",
"ECDSAWithP256AndSHA256",
"ECDSAWithP384AndSHA384",
"ECDSAWithP521AndSHA512",
@@ -507,6 +513,7 @@ var stdlib = map[string][]string{
"ConstraintViolationError",
"CreateCertificate",
"CreateCertificateRequest",
"CreateRevocationList",
"DSA",
"DSAWithSHA1",
"DSAWithSHA256",
@@ -581,6 +588,7 @@ var stdlib = map[string][]string{
"PublicKeyAlgorithm",
"PureEd25519",
"RSA",
"RevocationList",
"SHA1WithRSA",
"SHA256WithRSA",
"SHA256WithRSAPSS",
@@ -694,6 +702,7 @@ var stdlib = map[string][]string{
"String",
"Tx",
"TxOptions",
"Validator",
"Value",
"ValueConverter",
"Valuer",
@@ -2349,6 +2358,27 @@ var stdlib = map[string][]string{
"IMAGE_DIRECTORY_ENTRY_RESOURCE",
"IMAGE_DIRECTORY_ENTRY_SECURITY",
"IMAGE_DIRECTORY_ENTRY_TLS",
"IMAGE_DLLCHARACTERISTICS_APPCONTAINER",
"IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE",
"IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY",
"IMAGE_DLLCHARACTERISTICS_GUARD_CF",
"IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA",
"IMAGE_DLLCHARACTERISTICS_NO_BIND",
"IMAGE_DLLCHARACTERISTICS_NO_ISOLATION",
"IMAGE_DLLCHARACTERISTICS_NO_SEH",
"IMAGE_DLLCHARACTERISTICS_NX_COMPAT",
"IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE",
"IMAGE_DLLCHARACTERISTICS_WDM_DRIVER",
"IMAGE_FILE_32BIT_MACHINE",
"IMAGE_FILE_AGGRESIVE_WS_TRIM",
"IMAGE_FILE_BYTES_REVERSED_HI",
"IMAGE_FILE_BYTES_REVERSED_LO",
"IMAGE_FILE_DEBUG_STRIPPED",
"IMAGE_FILE_DLL",
"IMAGE_FILE_EXECUTABLE_IMAGE",
"IMAGE_FILE_LARGE_ADDRESS_AWARE",
"IMAGE_FILE_LINE_NUMS_STRIPPED",
"IMAGE_FILE_LOCAL_SYMS_STRIPPED",
"IMAGE_FILE_MACHINE_AM33",
"IMAGE_FILE_MACHINE_AMD64",
"IMAGE_FILE_MACHINE_ARM",
@@ -2371,6 +2401,25 @@ var stdlib = map[string][]string{
"IMAGE_FILE_MACHINE_THUMB",
"IMAGE_FILE_MACHINE_UNKNOWN",
"IMAGE_FILE_MACHINE_WCEMIPSV2",
"IMAGE_FILE_NET_RUN_FROM_SWAP",
"IMAGE_FILE_RELOCS_STRIPPED",
"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP",
"IMAGE_FILE_SYSTEM",
"IMAGE_FILE_UP_SYSTEM_ONLY",
"IMAGE_SUBSYSTEM_EFI_APPLICATION",
"IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
"IMAGE_SUBSYSTEM_EFI_ROM",
"IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER",
"IMAGE_SUBSYSTEM_NATIVE",
"IMAGE_SUBSYSTEM_NATIVE_WINDOWS",
"IMAGE_SUBSYSTEM_OS2_CUI",
"IMAGE_SUBSYSTEM_POSIX_CUI",
"IMAGE_SUBSYSTEM_UNKNOWN",
"IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION",
"IMAGE_SUBSYSTEM_WINDOWS_CE_GUI",
"IMAGE_SUBSYSTEM_WINDOWS_CUI",
"IMAGE_SUBSYSTEM_WINDOWS_GUI",
"IMAGE_SUBSYSTEM_XBOX",
"ImportDirectory",
"NewFile",
"Open",
@@ -4188,6 +4237,7 @@ var stdlib = map[string][]string{
"DevNull",
"Environ",
"ErrClosed",
"ErrDeadlineExceeded",
"ErrExist",
"ErrInvalid",
"ErrNoDeadline",
@@ -4646,6 +4696,7 @@ var stdlib = map[string][]string{
"ErrRange",
"ErrSyntax",
"FormatBool",
"FormatComplex",
"FormatFloat",
"FormatInt",
"FormatUint",
@@ -4655,6 +4706,7 @@ var stdlib = map[string][]string{
"Itoa",
"NumError",
"ParseBool",
"ParseComplex",
"ParseFloat",
"ParseInt",
"ParseUint",

12
vendor/modules.txt vendored
View File

@@ -515,7 +515,12 @@ github.com/willf/bitset
# github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
## explicit
github.com/xtgo/uuid
# go.etcd.io/bbolt v1.3.4
# github.com/ziutek/mymysql v1.5.4
## explicit
# github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a
## explicit
# go.etcd.io/bbolt v1.3.5
## explicit
go.etcd.io/bbolt
# go.uber.org/atomic v1.6.0
go.uber.org/atomic
@@ -612,7 +617,7 @@ golang.org/x/text/secure/bidirule
golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
# golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f
# golang.org/x/tools v0.0.0-20200806022845-90696ccdc692
## explicit
golang.org/x/tools/go/ast/astutil
golang.org/x/tools/go/gcexportdata
@@ -630,7 +635,8 @@ golang.org/x/tools/internal/gopathwalk
golang.org/x/tools/internal/imports
golang.org/x/tools/internal/packagesinternal
golang.org/x/tools/internal/typesinternal
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
## explicit
golang.org/x/xerrors
golang.org/x/xerrors/internal
# google.golang.org/appengine v1.6.6