diff --git a/setup/cmd/kots-field-labs/main.go b/setup/cmd/kots-field-labs/main.go index d30beb8ac..6157d6a1c 100644 --- a/setup/cmd/kots-field-labs/main.go +++ b/setup/cmd/kots-field-labs/main.go @@ -40,7 +40,7 @@ func HandleRequest(event fieldlabs.LambdaEvent) error { APIOrigin: "https://api.replicated.com/vendor", GraphQLOrigin: "https://g.replicated.com/graphql", KURLSHOrigin: "https://kurl.sh", - IDOrigin: "https://api.replicated.com/vendor", + IDOrigin: "https://api.replicated.com", } action, ok := actions[event.Action] diff --git a/setup/cmd/kots-field-labs/param.go b/setup/cmd/kots-field-labs/param.go index 63ddb1098..6e68a6012 100644 --- a/setup/cmd/kots-field-labs/param.go +++ b/setup/cmd/kots-field-labs/param.go @@ -60,7 +60,7 @@ func GetParams() (*fieldlabs.Params, error) { params.KURLSHOrigin = "https://kurl.sh" } if params.IDOrigin == "" { - params.IDOrigin = "https://api.replicated.com/vendor" + params.IDOrigin = "https://api.replicated.com" } actionString := os.Getenv("REPLICATED_ACTION") @@ -119,7 +119,7 @@ func getLoginResponse(params *fieldlabs.Params) (*string, error) { return nil, errors.Wrap(err, "marshal login params") } - loginReq, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/login", params.IDOrigin), bytes.NewBuffer(loginBody)) + loginReq, err := http.NewRequest("POST", fmt.Sprintf("%s/vendor/v1/login", params.IDOrigin), bytes.NewBuffer(loginBody)) if err != nil { return nil, errors.Wrap(err, "build login request") } @@ -131,9 +131,9 @@ func getLoginResponse(params *fieldlabs.Params) (*string, error) { } defer loginResp.Body.Close() - if loginResp.StatusCode != 201 { + if loginResp.StatusCode != 201 && loginResp.StatusCode != 200 { body, _ := ioutil.ReadAll(loginResp.Body) - return nil, fmt.Errorf("GET /policies %d: %s", loginResp.StatusCode, body) + return nil, fmt.Errorf("Parsing login response `/vendor/v1/login` %d: %s", loginResp.StatusCode, body) } bodyBytes, err := ioutil.ReadAll(loginResp.Body) if err != nil { diff --git a/setup/pkg/fieldlabs/environment_create.go b/setup/pkg/fieldlabs/environment_create.go index 0b6066050..f4b5ab009 100644 --- a/setup/pkg/fieldlabs/environment_create.go +++ b/setup/pkg/fieldlabs/environment_create.go @@ -1,6 +1,7 @@ package fieldlabs import ( + "encoding/json" "fmt" "io" "io/ioutil" @@ -103,6 +104,9 @@ func (e *EnvironmentManager) Ensure(track *TrackSpec) error { if err != nil { return errors.Wrap(err, "add member") } + memberJson, _ := json.Marshal(members) + policyJson, _ := json.Marshal(policies) + e.Log.ActionWithSpinner("Added member: %s %s", memberJson, policyJson) err = e.createVendorTrack(*app, *track) if err != nil { @@ -119,85 +123,90 @@ func (e *EnvironmentManager) createVendorTrack(app types.App, trackSpec TrackSpe appTrackSlug := fmt.Sprintf("%s-%s", app.Slug, track.Spec.Slug) e.Log.ActionWithSpinner("Provision track %s", appTrackSlug) - // get the stable channel to assign for customer - channel, err := e.getChannel(track) - if err != nil { - return errors.Wrapf(err, "get Stable channel") - } - track.Status.Channel = channel - - // Create customer - if trackSpec.Customer != "" { - customer, err := e.getOrCreateCustomer(track) - if err != nil { - return errors.Wrapf(err, "create customer for track %q app %q", trackSpec.Slug, app.Slug) - } - track.Status.Customer = customer - } + // get the stable channel to assign for customer + e.Log.ActionWithSpinner("Get stable channel") + channel, err := e.getChannel(track) + if err != nil { + return errors.Wrapf(err, "get Stable channel") + } + track.Status.Channel = channel + e.Log.ActionWithSpinner("Got channel: %s", channel) + + // Create customer + e.Log.ActionWithSpinner("Create customer") + if trackSpec.Customer != "" { + customer, err := e.getOrCreateCustomer(track) + if err != nil { + return errors.Wrapf(err, "create customer for track %q app %q", trackSpec.Slug, app.Slug) + } + track.Status.Customer = customer + } // load yaml for releases first to ensure directories exist - if trackSpec.YAMLDir != "" { - kotsYAML, err := readYAMLDir(fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) - - if err != nil { - return errors.Wrapf(err, "read yaml dir %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) - } - - for _, extraRelease := range track.Spec.ExtraReleases { - kotsYAML, err := readYAMLDir(fmt.Sprintf("%s/%s", e.VendorLoc, extraRelease.YAMLDir)) - if err != nil { - return errors.Wrapf(err, "read yaml dir %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) - } - track.Status.ExtraReleases = append(track.Status.ExtraReleases, ExtraReleaseStatus{ - Spec: extraRelease, - YAML: kotsYAML, - }) - - } - - release, err := e.Client.CreateRelease(app.ID, kotsYAML) - if err != nil { - return errors.Wrapf(err, "create release for %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) - } - - track.Status.Release = release - - err = e.Client.PromoteRelease(app.ID, release.Sequence, "0.1.0", trackSpec.Slug, false, channel.ID) - if err != nil { - return errors.Wrapf(err, "promote release %d to channel %q", release.Sequence, channel.Slug) - } - - for _, extraRelease := range track.Status.ExtraReleases { - releaseInfo, err := e.Client.CreateRelease(app.ID, extraRelease.YAML) - if err != nil { - return errors.Wrapf(err, "create release for %q", fmt.Sprintf("%s/%s", e.VendorLoc, extraRelease.Spec.YAMLDir)) - } - extraRelease.Release = releaseInfo - - if extraRelease.Spec.PromoteChannel != "" { - - continue - } - } - - if trackSpec.K8sInstallerYAMLPath != "" { - kurlYAML, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.K8sInstallerYAMLPath)) - if err != nil { - return errors.Wrapf(err, "read installer yaml %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.K8sInstallerYAMLPath)) - } - - installer, err := e.Client.CreateInstaller(app.ID, string(kurlYAML)) - if err != nil { - return errors.Wrapf(err, "create installer from %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.K8sInstallerYAMLPath)) - } - track.Status.Installer = installer - - err = e.Client.PromoteInstaller(app.ID, installer.Sequence, channel.ID, trackSpec.Slug) - if err != nil { - return errors.Wrapf(err, "promote installer %d to channel %q", installer.Sequence, channel.Slug) - } - } - } + if trackSpec.YAMLDir != "" { + kotsYAML, err := readYAMLDir(fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) + + if err != nil { + return errors.Wrapf(err, "read yaml dir %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) + } + + for _, extraRelease := range track.Spec.ExtraReleases { + kotsYAML, err := readYAMLDir(fmt.Sprintf("%s/%s", e.VendorLoc, extraRelease.YAMLDir)) + if err != nil { + return errors.Wrapf(err, "read yaml dir %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) + } + track.Status.ExtraReleases = append(track.Status.ExtraReleases, ExtraReleaseStatus{ + Spec: extraRelease, + YAML: kotsYAML, + }) + + } + + release, err := e.Client.CreateRelease(app.ID, kotsYAML) + if err != nil { + return errors.Wrapf(err, "create release for %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.YAMLDir)) + } + + track.Status.Release = release + + e.Log.ActionWithSpinner("Promote release") + err = e.Client.PromoteRelease(app.ID, release.Sequence, "0.1.0", trackSpec.Slug, false, channel.ID) + if err != nil { + return errors.Wrapf(err, "promote release %d to channel %q", release.Sequence, channel.Slug) + } + + for _, extraRelease := range track.Status.ExtraReleases { + releaseInfo, err := e.Client.CreateRelease(app.ID, extraRelease.YAML) + if err != nil { + return errors.Wrapf(err, "create release for %q", fmt.Sprintf("%s/%s", e.VendorLoc, extraRelease.Spec.YAMLDir)) + } + extraRelease.Release = releaseInfo + + if extraRelease.Spec.PromoteChannel != "" { + + continue + } + } + + if trackSpec.K8sInstallerYAMLPath != "" { + kurlYAML, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.K8sInstallerYAMLPath)) + if err != nil { + return errors.Wrapf(err, "read installer yaml %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.K8sInstallerYAMLPath)) + } + + installer, err := e.Client.CreateInstaller(app.ID, string(kurlYAML)) + if err != nil { + return errors.Wrapf(err, "create installer from %q", fmt.Sprintf("%s/%s", e.VendorLoc, trackSpec.K8sInstallerYAMLPath)) + } + track.Status.Installer = installer + + err = e.Client.PromoteInstaller(app.ID, installer.Sequence, channel.ID, trackSpec.Slug) + if err != nil { + return errors.Wrapf(err, "promote installer %d to channel %q", installer.Sequence, channel.Slug) + } + } + } + e.Log.ActionWithSpinner("Provisioned") return nil } @@ -242,8 +251,8 @@ func (e *EnvironmentManager) getOrCreateCustomer(track Track) (*types.Customer, } } - var createOpts = kotsclient.CreateCustomerOpts{ Name: track.Spec.Customer, AppID: track.Status.App.ID, Email: track.Spec.Customer + "@replicated-labs.com", ChannelID: track.Status.Channel.ID, ExpiresAt: OneWeek } - customer, err := e.Client.CreateCustomer(createOpts) + var createOpts = kotsclient.CreateCustomerOpts{Name: track.Spec.Customer, AppID: track.Status.App.ID, Email: track.Spec.Customer + "@replicated-labs.com", ChannelID: track.Status.Channel.ID, ExpiresAt: OneWeek} + customer, err := e.Client.CreateCustomer(createOpts) if err != nil { return nil, errors.Wrapf(err, "create customer for track %q app %q", track.Spec.Slug, track.Status.App.Slug) } diff --git a/setup/pkg/fieldlabs/environment_destroy.go b/setup/pkg/fieldlabs/environment_destroy.go index 66d404088..7b9b5d716 100644 --- a/setup/pkg/fieldlabs/environment_destroy.go +++ b/setup/pkg/fieldlabs/environment_destroy.go @@ -1,36 +1,48 @@ package fieldlabs import ( + "encoding/json" "github.com/pkg/errors" ) func (e *EnvironmentManager) Destroy(track *TrackSpec) error { + e.Log.Verbose() + e.Log.Debug("Destroying environment %s", e.Params.ParticipantId) + e.Log.Debug("Get policies") policies, err := e.getPolicies() if err != nil { return errors.Wrap(err, "get policies") } + + e.Log.Debug("Get member map") members, err := e.getMembersMap() if err != nil { return errors.Wrap(err, "get members") } inviteEmail := e.Params.ParticipantId + "@replicated-labs.com" + memberJson, _ := json.Marshal(members) + e.Log.Debug("Need to find %s in: %s", inviteEmail, memberJson) + e.Log.Debug("Delete member %s", inviteEmail) err = e.DeleteMember(members[inviteEmail].Id) if err != nil { return err } + e.Log.Debug("Delete policy id %s", policies[e.Params.ParticipantId]) err = e.DeletePolicyId(policies[e.Params.ParticipantId]) if err != nil { return err } // Delete the app + e.Log.Debug("List apps") apps, err := e.Client.ListApps() if err != nil { return errors.Wrapf(err, "list apps") } for _, app := range apps { if app.App.Name == track.Name+" "+e.Params.ParticipantId { + e.Log.Debug("Delete app %s", app.App.Name) err = e.Client.DeleteKOTSApp(app.App.ID) if err != nil { return errors.Wrapf(err, "delete app") diff --git a/setup/pkg/fieldlabs/members.go b/setup/pkg/fieldlabs/members.go index bb02bd528..ba921daed 100644 --- a/setup/pkg/fieldlabs/members.go +++ b/setup/pkg/fieldlabs/members.go @@ -20,6 +20,7 @@ type MemberList struct { // Get list of team members created with multi-player mode func (e *EnvironmentManager) GetMembers() ([]MemberList, error) { url := fmt.Sprintf("%s/v1/team/members", e.Params.IDOrigin) + e.Log.Debug(fmt.Sprintf("GET %s", url)) req, err := http.NewRequest( "GET", url, @@ -42,9 +43,10 @@ func (e *EnvironmentManager) GetMembers() ([]MemberList, error) { panic(err.Error()) } if resp.StatusCode != 200 { - return []MemberList{}, fmt.Errorf("GET /v1/team/members %d: %s", resp.StatusCode, body) + return []MemberList{}, fmt.Errorf("GET %d: %s", url, resp.StatusCode, body) } + e.Log.Debug(fmt.Sprintf("GET %s %d: %s", url, resp.StatusCode, body)) var members []MemberList err = json.Unmarshal([]byte(body), &members) if err != nil { @@ -60,8 +62,14 @@ func (e *EnvironmentManager) getMembersMap() (map[string]MemberList, error) { return nil, errors.Wrap(err, "get members") } + membersJson, _ := json.Marshal(members) + fmt.Sprintf("members: %s", membersJson) + membersMap := make(map[string]MemberList) for i := 0; i < len(members); i += 1 { + memberJson, _ := json.Marshal(members) + fmt.Sprintf("member: %s", memberJson) + fmt.Sprintf("member: %s", members[i].Email) membersMap[members[i].Email] = members[i] } return membersMap, nil @@ -69,15 +77,16 @@ func (e *EnvironmentManager) getMembersMap() (map[string]MemberList, error) { // Delete team members created with multi-player mode func (e *EnvironmentManager) DeleteMember(id string) error { - url := fmt.Sprintf("%s/v1/team/member?user_id=%s", e.Params.IDOrigin, id) + requestUrl := fmt.Sprintf("%s/v1/team/member?id=%s", e.Params.IDOrigin, id) + e.Log.Debug(fmt.Sprintf("DELETE %s", requestUrl)) req, err := http.NewRequest( "DELETE", - url, + requestUrl, nil, ) if err != nil { - return err + return errors.Wrap(err, fmt.Sprintf("DELETE %s", requestUrl)) } req.Header.Set("Authorization", e.Params.SessionToken) req.Header.Set("Accept", "application/json") @@ -91,8 +100,8 @@ func (e *EnvironmentManager) DeleteMember(id string) error { if err != nil { panic(err.Error()) } - if resp.StatusCode != 204 { - return fmt.Errorf("GET /v1/team/member %d: %s", resp.StatusCode, body) + if resp.StatusCode != 204 && resp.StatusCode != 200 { + return fmt.Errorf("DELETE %s %d: %s", requestUrl, resp.StatusCode, body) } return nil } @@ -128,93 +137,107 @@ func (e *EnvironmentManager) addMember(members map[string]MemberList, policies m return err } return nil +} +type VerifyResponse struct { + Token string `json:"token"` } -type AcceptBody struct { - InviteId string `json:"invite_id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Password string `json:"password"` - ReplaceAccount bool `json:"replace_account"` - FromTeamSelection bool `json:"from_team_selection"` +type SignupResponse struct { + Token string `json:"token"` } -func (e *EnvironmentManager) acceptInvite(invite *InvitedTeams, participantId string, vr *VerifyResponse) error { - h := sha256.Sum256([]byte(participantId)) - sum := fmt.Sprintf("%x", h) - ab := AcceptBody{InviteId: (*invite).Teams[0].InviteId, FirstName: "Repl", LastName: "Replicated", Password: string(sum[0:20]), ReplaceAccount: false, FromTeamSelection: true} - acceptBodyBytes, err := json.Marshal(ab) +func (e *EnvironmentManager) signupMember(inviteEmail string) (*SignupResponse, error) { + signupBody := map[string]string{ + "email": inviteEmail, + } + signupBodyBytes, err := json.Marshal(signupBody) if err != nil { - return errors.Wrap(err, "marshal accept body") + return nil, errors.Wrap(err, "marshal signup body") } + requestUrl := fmt.Sprintf("%s/vendor/v1/signup", e.Params.IDOrigin) req, err := http.NewRequest( "POST", - fmt.Sprintf("%s/v1/signup/accept-invite", e.Params.IDOrigin), - bytes.NewReader(acceptBodyBytes), + requestUrl, + bytes.NewReader(signupBodyBytes), ) if err != nil { - return errors.Wrap(err, "build accept request") + return nil, errors.Wrap(err, "build signup request") } req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { - return errors.Wrap(err, "send accept request") + return nil, errors.Wrap(err, "send signup request") } defer resp.Body.Close() if resp.StatusCode != 201 { body, _ := ioutil.ReadAll(resp.Body) - return fmt.Errorf("POST /v1/signup/accept-invite %d: %s", resp.StatusCode, body) + return nil, fmt.Errorf("POST %s %d: %s", requestUrl, resp.StatusCode, body) } - return nil -} + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "read body") + } + var body SignupResponse + if err := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&body); err != nil { + return nil, errors.Wrap(err, "decode body") + } + return &body, nil -type InvitedTeams struct { - Teams []struct { - Id string `json:"id"` - Name string `json:"name"` - InviteId string `json:"invite_id"` - } `json:"invited_teams"` } -func (e *EnvironmentManager) captureInvite(vr *VerifyResponse) (*InvitedTeams, error) { +func (e *EnvironmentManager) inviteMember(inviteEmail string, members map[string]MemberList, policies map[string]string) error { + if _, memberExists := members[inviteEmail]; memberExists { + // This should never happen? + return nil + } + inviteBody := map[string]string{ + "email": inviteEmail, + "policy_id": policies[e.Params.ParticipantId], + } + inviteBodyBytes, err := json.Marshal(inviteBody) + if err != nil { + return errors.Wrap(err, "marshal invite body") + } + requestUrl := fmt.Sprintf("%s/vendor/v1/team/invite", e.Params.IDOrigin) req, err := http.NewRequest( - "GET", - fmt.Sprintf("%s/v1/signup/teams", e.Params.IDOrigin), - nil, + "POST", + requestUrl, + bytes.NewReader(inviteBodyBytes), ) if err != nil { - return nil, errors.Wrap(err, "build signup teams request") + return errors.Wrap(err, "build invite request") } + req.Header.Set("Authorization", e.Params.SessionToken) req.Header.Set("Accept", "application/json") - req.Header.Set("Authorization", vr.Token) + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, errors.Wrap(err, "send signup teams request") + return errors.Wrap(err, fmt.Sprintf("send invite request: %s", requestUrl)) } defer resp.Body.Close() - - if resp.StatusCode != 200 { - body, _ := ioutil.ReadAll(resp.Body) - return nil, fmt.Errorf("POST /v1/signup/teams %d: %s", resp.StatusCode, body) - } - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, errors.Wrap(err, "read body") + // rate limit returned when already invited + if resp.StatusCode == 429 { + e.Log.ActionWithoutSpinner("Skipping invite %q due to 429 error", inviteEmail) + return nil } - var body InvitedTeams - if err := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&body); err != nil { - return nil, errors.Wrap(err, "decode body") + if resp.StatusCode != 204 { + body, _ := ioutil.ReadAll(resp.Body) + return fmt.Errorf("POST %s %d: %s", requestUrl, resp.StatusCode, body) } - return &body, nil + return nil } -type VerifyResponse struct { - Token string `json:"token"` +type InvitedTeams struct { + Teams []struct { + Id string `json:"id"` + Name string `json:"name"` + InviteId string `json:"invite_id"` + } `json:"invited_teams"` } func (e *EnvironmentManager) verifyMember(sr *SignupResponse) (*VerifyResponse, error) { @@ -225,13 +248,14 @@ func (e *EnvironmentManager) verifyMember(sr *SignupResponse) (*VerifyResponse, if err != nil { return nil, errors.Wrap(err, "marshal verify body") } + requestUrl := fmt.Sprintf("%s/vendor/v1/signup/verify", e.Params.IDOrigin) req, err := http.NewRequest( "POST", - fmt.Sprintf("%s/v1/signup/verify", e.Params.IDOrigin), + requestUrl, bytes.NewReader(verifyBodyBytes), ) if err != nil { - return nil, errors.Wrap(err, "build verify request") + return nil, errors.Wrap(err, fmt.Sprintf("build verify request: %s", requestUrl)) } req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") @@ -244,7 +268,7 @@ func (e *EnvironmentManager) verifyMember(sr *SignupResponse) (*VerifyResponse, if resp.StatusCode != 201 { body, _ := ioutil.ReadAll(resp.Body) - return nil, fmt.Errorf("POST /v1/signup/verify %d: %s", resp.StatusCode, body) + return nil, fmt.Errorf("POST %s %d: %s", requestUrl, resp.StatusCode, body) } bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -257,89 +281,80 @@ func (e *EnvironmentManager) verifyMember(sr *SignupResponse) (*VerifyResponse, return &body, nil } -type SignupResponse struct { - Token string `json:"token"` -} - -func (e *EnvironmentManager) signupMember(inviteEmail string) (*SignupResponse, error) { - signupBody := map[string]string{ - "email": inviteEmail, - } - signupBodyBytes, err := json.Marshal(signupBody) - if err != nil { - return nil, errors.Wrap(err, "marshal signup body") - } +func (e *EnvironmentManager) captureInvite(vr *VerifyResponse) (*InvitedTeams, error) { + e.Log.Verbose() + requestUrl := fmt.Sprintf("%s/vendor/v1/signup/teams", e.Params.IDOrigin) req, err := http.NewRequest( - "POST", - fmt.Sprintf("%s/v1/signup", e.Params.IDOrigin), - bytes.NewReader(signupBodyBytes), + "GET", + requestUrl, + nil, ) if err != nil { - return nil, errors.Wrap(err, "build signup request") + return nil, errors.Wrap(err, "build signup teams request") } req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", vr.Token) resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, errors.Wrap(err, "send signup request") + return nil, errors.Wrap(err, "getting the invite") } defer resp.Body.Close() - if resp.StatusCode != 201 { + if resp.StatusCode != 200 { body, _ := ioutil.ReadAll(resp.Body) - return nil, fmt.Errorf("POST /v1/signup %d: %s", resp.StatusCode, body) + return nil, fmt.Errorf("GET %s %d: %s", requestUrl, resp.StatusCode, body) } bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, errors.Wrap(err, "read body") } - var body SignupResponse + var body InvitedTeams if err := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&body); err != nil { return nil, errors.Wrap(err, "decode body") } return &body, nil +} +type AcceptBody struct { + InviteId string `json:"invite_id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Password string `json:"password"` + ReplaceAccount bool `json:"replace_account"` + FromTeamSelection bool `json:"from_team_selection"` } -func (e *EnvironmentManager) inviteMember(inviteEmail string, members map[string]MemberList, policies map[string]string) error { - if _, memberExists := members[inviteEmail]; memberExists { - // This should never happen? - return nil - } - inviteBody := map[string]string{ - "email": inviteEmail, - "policy_id": policies[e.Params.ParticipantId], - } - inviteBodyBytes, err := json.Marshal(inviteBody) +func (e *EnvironmentManager) acceptInvite(invite *InvitedTeams, participantId string, vr *VerifyResponse) error { + h := sha256.Sum256([]byte(participantId)) + sum := fmt.Sprintf("%x", h) + ab := AcceptBody{InviteId: (*invite).Teams[0].InviteId, FirstName: "Instruqt", LastName: "Participant", Password: string(sum[0:20]), ReplaceAccount: false, FromTeamSelection: true} + acceptBodyBytes, err := json.Marshal(ab) if err != nil { - return errors.Wrap(err, "marshal invite body") + return errors.Wrap(err, "marshal accept body") } + + requestUrl := fmt.Sprintf("%s/vendor/v1/signup/accept-invite", e.Params.IDOrigin) req, err := http.NewRequest( "POST", - fmt.Sprintf("%s/v1/team/invite", e.Params.IDOrigin), - bytes.NewReader(inviteBodyBytes), + requestUrl, + bytes.NewReader(acceptBodyBytes), ) if err != nil { - return errors.Wrap(err, "build invite request") + return errors.Wrap(err, "build accept request") } - req.Header.Set("Authorization", e.Params.SessionToken) req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { - return errors.Wrap(err, "send invite request") + return errors.Wrap(err, fmt.Sprintf("send accept request: %s", requestUrl)) } defer resp.Body.Close() - // rate limit returned when already invited - if resp.StatusCode == 429 { - e.Log.ActionWithoutSpinner("Skipping invite %q due to 429 error", inviteEmail) - return nil - } - if resp.StatusCode != 204 { + + if resp.StatusCode != 201 { body, _ := ioutil.ReadAll(resp.Body) - return fmt.Errorf("POST /team/invite %d: %s", resp.StatusCode, body) + return fmt.Errorf("POST %s %d: %s", requestUrl, resp.StatusCode, body) } return nil } diff --git a/setup/pkg/fieldlabs/policy.go b/setup/pkg/fieldlabs/policy.go index 3fd6df3c6..e3b1a20a6 100644 --- a/setup/pkg/fieldlabs/policy.go +++ b/setup/pkg/fieldlabs/policy.go @@ -37,6 +37,10 @@ type PolicyResourcesV1 struct { Denied []string `json:"denied"` } +type PolicyListResponse struct { + Policies []PolicyListItem `json:"policies"` +} + type PolicyListItem struct { Id string `json:"id"` Name string `json:"name"` @@ -47,9 +51,10 @@ type PolicyUpdate struct { } func (e *EnvironmentManager) getPolicies() (map[string]string, error) { + requestUrl := fmt.Sprintf("%s/vendor/v1/policies", e.Params.IDOrigin) req, err := http.NewRequest( "GET", - fmt.Sprintf("%s/v1/policies", e.Params.IDOrigin), + requestUrl, nil, ) if err != nil { @@ -67,17 +72,17 @@ func (e *EnvironmentManager) getPolicies() (map[string]string, error) { panic(err.Error()) } if resp.StatusCode != 200 { - return nil, fmt.Errorf("GET /v1/policies %d: %s", resp.StatusCode, body) + return nil, fmt.Errorf("GET %s %d: %s", requestUrl, resp.StatusCode, body) } - var policies []PolicyListItem + var policies PolicyListResponse err = json.Unmarshal([]byte(body), &policies) if err != nil { - return nil, errors.Wrap(err, "list policies unmarshal") + return nil, errors.Wrap(err, fmt.Sprintf("list policies unmarshal %s", body)) } policiesMap := make(map[string]string) - for i := 0; i < len(policies); i += 1 { - policiesMap[policies[i].Name] = policies[i].Id + for i := 0; i < len(policies.Policies); i += 1 { + policiesMap[policies.Policies[i].Name] = policies.Policies[i].Id } return policiesMap, nil } @@ -111,9 +116,10 @@ func (e *EnvironmentManager) createRBAC(app types.App, policies map[string]strin if err != nil { return errors.Wrap(err, "marshal rbac body") } + requestUrl := fmt.Sprintf("%s/vendor/v1/policy", e.Params.IDOrigin) req, err := http.NewRequest( "POST", - fmt.Sprintf("%s/v1/policy", e.Params.IDOrigin), + requestUrl, bytes.NewReader(rbacBodyBytes), ) if err != nil { @@ -128,23 +134,62 @@ func (e *EnvironmentManager) createRBAC(app types.App, policies map[string]strin return errors.Wrap(err, "send rbac request") } defer resp.Body.Close() - if resp.StatusCode != 201 { + if resp.StatusCode != 201 && resp.StatusCode != 200 { body, _ := ioutil.ReadAll(resp.Body) - return fmt.Errorf("POST /v1/policy %d: %s", resp.StatusCode, body) + return fmt.Errorf("POST %s %d: %s", requestUrl, resp.StatusCode, body) } return nil } +// Get a single policy by id +func (e *EnvironmentManager) GetPolicyId(id string) (*Policy, error) { + requestUrl := fmt.Sprintf("%s/vendor/v1/policy/%s", e.Params.IDOrigin, id) + e.Log.Debug(fmt.Sprintf("GET %s", requestUrl)) + req, err := http.NewRequest( + "GET", + requestUrl, + nil, + ) + + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf("GET %s", requestUrl)) + } + req.Header.Set("Authorization", e.Params.SessionToken) + req.Header.Set("Accept", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf("send get request: %s", requestUrl)) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err.Error()) + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("GET %s %d %s", requestUrl, resp.StatusCode, body) + } + var policy Policy + err = json.Unmarshal([]byte(body), &policy) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf("unmarshal %s", body)) + } + return &policy, nil +} + // Delete policies create through multi-player mode func (e *EnvironmentManager) DeletePolicyId(id string) error { - url := fmt.Sprintf("%s/v1/policy/%s", e.Params.IDOrigin, id) + requestUrl := fmt.Sprintf("%s/vendor/v1/policy/%s", e.Params.IDOrigin, id) + e.Log.Debug(fmt.Sprintf("DELETE %s", requestUrl)) req, err := http.NewRequest( "DELETE", - url, + requestUrl, nil, ) + policy, _ := e.GetPolicyId(id) + policyJson, _ := json.Marshal(policy) + e.Log.Debug(fmt.Sprintf("policy %s contains %s", policy.Name, policyJson)) if err != nil { return err } @@ -160,8 +205,8 @@ func (e *EnvironmentManager) DeletePolicyId(id string) error { if err != nil { panic(err.Error()) } - if resp.StatusCode != 204 { - return fmt.Errorf("GET /v1/policy %d: %s", resp.StatusCode, body) + if resp.StatusCode != 204 && resp.StatusCode != 200 { + return fmt.Errorf("DELETE %s %d: %s", requestUrl, resp.StatusCode, body) } return nil }