diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..9875d33 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,56 @@ +linters-settings: + gofumpt: + extra-rules: true + +linters: + enable: + # default linters + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - unused + + # additional linters + - bodyclose + - gocritic + - gocyclo + # - goerr113 + - gofmt + - goimports + - revive + - gomnd + - govet + - misspell + - noctx + - stylecheck + - whitespace + - wsl + + # - bod +issues: + exclude: + # Default excludes from `golangci-lint run --help` with EXC0002 removed + # EXC0001 errcheck: Almost all programs ignore errors on these functions and in most cases it's ok + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked + # EXC0002 golint: Annoying issue about not having a comment. The rare codebase has such comments + # - (comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form) + # EXC0003 golint: False positive when tests are defined in package 'test' + - func name will be used as test\.Test.* by other packages, and that stutters; consider calling this + # EXC0004 govet: Common false positives + - (possible misuse of unsafe.Pointer|should have signature) + # EXC0005 staticcheck: Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore + - ineffective break statement. Did you mean to break out of the outer loop + # EXC0006 gosec: Too many false-positives on 'unsafe' usage + - Use of unsafe calls should be audited + # EXC0007 gosec: Too many false-positives for parametrized shell calls + - Subprocess launch(ed with variable|ing should be audited) + # EXC0008 gosec: Duplicated errcheck checks + - (G104|G307) + # EXC0009 gosec: Too many issues in popular repos + - (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less) + # EXC0010 gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)' + - Potential file inclusion via variable + exclude-use-default: false diff --git a/internal/metal/models/organizations.go b/internal/metal/models/organizations.go index c2cd095..e99055b 100644 --- a/internal/metal/models/organizations.go +++ b/internal/metal/models/organizations.go @@ -2,6 +2,7 @@ package models import "go.infratographer.com/x/gidx" +// OrganizationDetails contains the organization and membership information. type OrganizationDetails struct { ID string `json:"id"` Name string `json:"name"` @@ -9,6 +10,7 @@ type OrganizationDetails struct { Projects []*ProjectDetails `json:"projects"` } +// PrefixedID returns the prefixed id for the organization. func (d *OrganizationDetails) PrefixedID() gidx.PrefixedID { if d.ID == "" { return gidx.NullPrefixedID diff --git a/internal/metal/models/users.go b/internal/metal/models/users.go index ecb1c4d..8c75d7c 100644 --- a/internal/metal/models/users.go +++ b/internal/metal/models/users.go @@ -11,7 +11,7 @@ const ( // MetalUserIssuer is the issuer that is used for metal api token users. MetalUserIssuer = "https://auth.equinix.com/" - // MetaluserIssuerIDPrefix is the issuer id prefix added by the issuer. + // MetalUserIssuerIDPrefix is the issuer id prefix added by the issuer. MetalUserIssuerIDPrefix = "auth|" ) diff --git a/internal/metal/providers/emapi/memberships.go b/internal/metal/providers/emapi/memberships.go index 5ea6c48..6e2b565 100644 --- a/internal/metal/providers/emapi/memberships.go +++ b/internal/metal/providers/emapi/memberships.go @@ -10,7 +10,7 @@ type Roles []string // Memberships contains a list of memberships type Memberships []*Membership -// ToDetailsWithOrganizationDetails convers the memberships to generic membership models with organization details. +// ToDetailsWithOrganizationDetails converts the memberships to generic membership models with organization details. func (m Memberships) ToDetailsWithOrganizationDetails(orgDetails *models.OrganizationDetails) []*models.Membership[models.OrganizationDetails] { memberships := make([]*models.Membership[models.OrganizationDetails], len(m)) @@ -31,7 +31,7 @@ func (m Memberships) ToDetailsWithOrganizationDetails(orgDetails *models.Organiz return memberships } -// ToDetailsWithProjectDetails convers the memberships to generic membership models with project details. +// ToDetailsWithProjectDetails converts the memberships to generic membership models with project details. func (m Memberships) ToDetailsWithProjectDetails(projDetails *models.ProjectDetails) []*models.Membership[models.ProjectDetails] { memberships := make([]*models.Membership[models.ProjectDetails], len(m)) @@ -60,7 +60,7 @@ type Membership struct { User *User `json:"user"` } -// ToDetailsWithOrganizationDetails convers the membership to generic membership model with organization details. +// ToDetailsWithOrganizationDetails converts the membership to generic membership model with organization details. func (m *Membership) ToDetailsWithOrganizationDetails(orgDetails *models.OrganizationDetails) *models.Membership[models.OrganizationDetails] { if m.ID == "" { return nil @@ -74,7 +74,7 @@ func (m *Membership) ToDetailsWithOrganizationDetails(orgDetails *models.Organiz } } -// ToDetailsWithOrganizationDetails convers the membership to generic membership model with organization details. +// ToDetailsWithProjectDetails converts the membership to generic membership model with organization details. func (m *Membership) ToDetailsWithProjectDetails(projDetails *models.ProjectDetails) *models.Membership[models.ProjectDetails] { if m.ID == "" { return nil diff --git a/internal/metal/providers/emapi/organizations.go b/internal/metal/providers/emapi/organizations.go index 5c695a8..35c1124 100644 --- a/internal/metal/providers/emapi/organizations.go +++ b/internal/metal/providers/emapi/organizations.go @@ -75,7 +75,7 @@ func (o *Organization) ToDetails() *models.OrganizationDetails { func (c *Client) getOrganizationWithMemberships(ctx context.Context, id string) (*Organization, error) { var org Organization - _, err := c.DoRequest(ctx, http.MethodGet, organizationsPath+"/"+id+"?include=memberships.user", nil, &org) + _, err := c.DoRequest(ctx, http.MethodGet, organizationsPath+"/"+id+"?include=memberships.user", nil, &org) // nolint:bodyclose // body is closed by Do json decode. if err != nil { return nil, fmt.Errorf("error loading organization: %w", err) } diff --git a/internal/metal/providers/emapi/projects.go b/internal/metal/providers/emapi/projects.go index 080b8fe..5dc27c5 100644 --- a/internal/metal/providers/emapi/projects.go +++ b/internal/metal/providers/emapi/projects.go @@ -75,7 +75,7 @@ func (p *Project) ToDetails() *models.ProjectDetails { func (c *Client) getProjectWithMemberships(ctx context.Context, id string) (*Project, error) { var project Project - _, err := c.DoRequest(ctx, http.MethodGet, projectsPath+"/"+id+"?include=memberships.user", nil, &project) + _, err := c.DoRequest(ctx, http.MethodGet, projectsPath+"/"+id+"?include=memberships.user", nil, &project) // nolint:bodyclose // body is closed by Do json decode. if err != nil { return nil, fmt.Errorf("error loading organization: %w", err) } diff --git a/internal/metal/providers/emapi/users.go b/internal/metal/providers/emapi/users.go index b8f8090..96742f5 100644 --- a/internal/metal/providers/emapi/users.go +++ b/internal/metal/providers/emapi/users.go @@ -71,7 +71,7 @@ func (u *User) ToDetails() *models.UserDetails { func (c *Client) getUser(ctx context.Context, id string) (*User, error) { var user User - _, err := c.DoRequest(ctx, http.MethodGet, usersPath+"/"+id, nil, &user) + _, err := c.DoRequest(ctx, http.MethodGet, usersPath+"/"+id, nil, &user) // nolint:bodyclose // body is closed by Do json decode. if err != nil { return nil, fmt.Errorf("error loading organization: %w", err) } diff --git a/internal/permissions/assignments.go b/internal/permissions/assignments.go index f2dadd4..895a4e2 100644 --- a/internal/permissions/assignments.go +++ b/internal/permissions/assignments.go @@ -38,7 +38,7 @@ func (c *Client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberI var response RoleAssignResponse - if _, err = c.DoRequest(ctx, http.MethodPost, path, body, &response); err != nil { + if _, err = c.DoRequest(ctx, http.MethodPost, path, body, &response); err != nil { // nolint:bodyclose // body is closed by Do json decode. return err } @@ -62,7 +62,7 @@ func (c *Client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, membe var response RoleAssignResponse - if _, err = c.DoRequest(ctx, http.MethodDelete, path, body, &response); err != nil { + if _, err = c.DoRequest(ctx, http.MethodDelete, path, body, &response); err != nil { // nolint:bodyclose // body is closed by Do json decode. return err } @@ -79,7 +79,7 @@ func (c *Client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID var response roleAssignmentData - if _, err := c.DoRequest(ctx, http.MethodGet, path, nil, &response); err != nil { + if _, err := c.DoRequest(ctx, http.MethodGet, path, nil, &response); err != nil { // nolint:bodyclose // body is closed by Do json decode. return nil, err } diff --git a/internal/permissions/client.go b/internal/permissions/client.go index 1ae0f20..c6ab9ad 100644 --- a/internal/permissions/client.go +++ b/internal/permissions/client.go @@ -18,7 +18,7 @@ import ( const defaultPermissionsURL = "https://permissions-api.hollow-a.sv15.metalkube.net" var defaultHTTPClient = &http.Client{ - Timeout: 5 * time.Second, + Timeout: 5 * time.Second, // nolint:gomnd // unexported } // Client is the permissions client. @@ -104,6 +104,7 @@ func encodeJSON(v any) (*bytes.Buffer, error) { return &buff, nil } +// NewClient creats a new permissions client. func NewClient(token string, options ...Option) (*Client, error) { client := &Client{ logger: zap.NewNop().Sugar(), diff --git a/internal/permissions/relationships.go b/internal/permissions/relationships.go index 50cf314..a9e8f8d 100644 --- a/internal/permissions/relationships.go +++ b/internal/permissions/relationships.go @@ -47,7 +47,7 @@ func (c *Client) DeleteResourceRelationship(ctx context.Context, resourceID gidx var response ResourceRelationshipDeleteResponse - if _, err := c.DoRequest(ctx, http.MethodDelete, path, body, &response); err != nil { + if _, err := c.DoRequest(ctx, http.MethodDelete, path, body, &response); err != nil { // nolint:bodyclose // closed by Do on json decode. return err } @@ -75,11 +75,12 @@ func (c *Client) ListResourceRelationships(ctx context.Context, resourceID gidx. Data []resourceRelationship `json:"data"` } - if _, err := c.DoRequest(ctx, http.MethodGet, url.String(), nil, &response); err != nil { + if _, err := c.DoRequest(ctx, http.MethodGet, url.String(), nil, &response); err != nil { // nolint:bodyclose // closed by Do on json decode. return nil, err } data := make([]ResourceRelationship, len(response.Data)) + for i, entry := range response.Data { var ( resID, subID gidx.PrefixedID diff --git a/internal/permissions/roles.go b/internal/permissions/roles.go index 0a1e4fa..cf1a884 100644 --- a/internal/permissions/roles.go +++ b/internal/permissions/roles.go @@ -46,7 +46,7 @@ func (c *Client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, act var response ResourceRoleCreateResponse - if _, err = c.DoRequest(ctx, http.MethodPost, path, body, &response); err != nil { + if _, err = c.DoRequest(ctx, http.MethodPost, path, body, &response); err != nil { // nolint:bodyclose // closed by Do on json decode. return gidx.NullPrefixedID, err } @@ -64,7 +64,7 @@ func (c *Client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error { var response ResourceRoleDeleteResponse - if _, err := c.DoRequest(ctx, http.MethodDelete, path, nil, &response); err != nil { + if _, err := c.DoRequest(ctx, http.MethodDelete, path, nil, &response); err != nil { // nolint:bodyclose // closed by Do on json decode. return err } @@ -83,7 +83,7 @@ func (c *Client) ListResourceRoles(ctx context.Context, resourceID gidx.Prefixed Data ResourceRoles `json:"data"` } - if _, err := c.DoRequest(ctx, http.MethodGet, path, nil, &response); err != nil { + if _, err := c.DoRequest(ctx, http.MethodGet, path, nil, &response); err != nil { // nolint:bodyclose // closed by Do on json decode. return nil, err } diff --git a/internal/pubsub/subscriber.go b/internal/pubsub/subscriber.go index d4642fa..eefb034 100644 --- a/internal/pubsub/subscriber.go +++ b/internal/pubsub/subscriber.go @@ -107,6 +107,7 @@ func (s Subscriber) listen(messages <-chan *message.Message, wg *sync.WaitGroup) for msg := range messages { s.logger.Infow("processing event", "event.id", msg.UUID) + if err := s.processEvent(msg); err != nil { s.logger.Warn("Failed to process msg: ", err) diff --git a/internal/service/process_relationships.go b/internal/service/process_relationships.go index ebee2cd..6e13a52 100644 --- a/internal/service/process_relationships.go +++ b/internal/service/process_relationships.go @@ -193,6 +193,7 @@ func (s *service) getRelationshipMap(ctx context.Context, resource IDPrefixableR } parents := make(map[gidx.PrefixedID]RelationshipType, len(liveResource)) + for _, relationship := range liveResource { if relationship.Relation != string(RelateParent) { continue @@ -202,6 +203,7 @@ func (s *service) getRelationshipMap(ctx context.Context, resource IDPrefixableR } subject := make(map[gidx.PrefixedID]RelationshipType, len(liveSubject)) + for _, relationship := range liveSubject { subject[relationship.ResourceID] = RelationshipType(relationship.Relation) } diff --git a/internal/service/users.go b/internal/service/users.go index 02ff9cd..66c08b5 100644 --- a/internal/service/users.go +++ b/internal/service/users.go @@ -59,6 +59,7 @@ func (s *service) AssignUser(ctx context.Context, userID gidx.PrefixedID, resour }, true) totalResources++ + rolesChanged += roles assignmentsChanged += assignments }