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

Release Logger, Bugfix, Workload Identity #112

Merged
merged 6 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/radix-cost-allocation-api-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,97 @@ jobs:
env:
REF: ${{ github. sha }}
run: docker build -t radix-cost-allocation-api:${REF##*/} .

lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install dependencies
run: go mod download
- name: Install GolangCI Lint
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2

- name: golangci-lint
run: golangci-lint run --timeout=30m --max-same-issues=0 --out-format=github-actions

test:
name: Unit Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install dependencies
run: go mod download
- name: Run Tests
run: go test -cover `go list ./...`

test-swagger:
name: Test Swagger
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: git fetch --no-tags --no-recurse-submodules --depth=1 origin master:master
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install dependencies
run: go mod download
- name: Install Swagger
run: go install github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5
- name: Generate Swagger
run: swagger generate spec -o ./swagger.json --scan-models --exclude-deps --exclude=github.com/equinor/radix-cost-allocation-api/models/radix_api/generated_client/models
- name: Validate no changes
run: diff ./swagger.json ./swaggerui/html/swagger.json
- name: Check breaking changes
if: always()
id: breaking
continue-on-error: true
run: swagger diff --break <(git show master:swaggerui/html/swagger.json) swagger.json > /tmp/swagger_breaking_changes.txt
- name: Add comment
if: failure() && steps.breaking.outcome == 'failure'
uses: mshick/add-pr-comment@v2
with:
message-id: breaking-comment
preformatted: true
message-path: /tmp/swagger_breaking_changes.txt

test-radixconfig:
name: Test RadixConfig
runs-on: ubuntu-latest
steps:
- name: 'Fake TOKEN FOR RADIX CLI'
run: echo "APP_SERVICE_ACCOUNT_TOKEN=hello-world" >> $GITHUB_ENV
- uses: actions/checkout@v4
- run: git fetch --no-tags --no-recurse-submodules --depth=1 origin master:master

- run: make generate-radixconfig-envs
- name: Test radixconfig template changes
run: git diff --exit-code

- name: 'Validate C2'
uses: equinor/radix-github-actions@v1
with:
args: validate radix-config --config-file radixconfig.c2.yaml

- name: 'Validate DEV'
uses: equinor/radix-github-actions@v1
with:
args: validate radix-config --config-file radixconfig.dev.yaml

- name: 'Validate Playground'
uses: equinor/radix-github-actions@v1
with:
args: validate radix-config --config-file radixconfig.playground.yaml

- name: 'Validate Platform'
uses: equinor/radix-github-actions@v1
with:
args: validate radix-config --config-file radixconfig.platform.yaml
8 changes: 1 addition & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
FROM golang:1.21-alpine3.18 as builder
FROM docker.io/golang:1.21-alpine3.18 as builder
ENV GO111MODULE=on

RUN apk update && \
apk add bash jq alpine-sdk sed gawk git ca-certificates curl mc && \
apk add --no-cache gcc musl-dev
RUN go install honnef.co/go/tools/cmd/staticcheck@2023.1.3

WORKDIR /go/src/github.com/equinor/radix-cost-allocation-api/

Expand All @@ -15,11 +14,6 @@ RUN go mod download
# copy api code
COPY . .

# lint and unit tests
RUN staticcheck ./... && \
go vet ./... && \
CGO_ENABLED=0 GOOS=linux go test ./...

# Build radix cost allocation API go project
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -a -installsuffix cgo -o /usr/local/bin/radix-cost-allocation-api

Expand Down
43 changes: 35 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,51 @@ build: $(BINS)

.PHONY: test
test:
go test -cover ./...
PRETTY_PRINT=yes go test -cover ./...

.PHONY: lint
lint: bootstrap
golangci-lint run --max-same-issues 0

.PHONY: generate-radix-api-client
generate-radix-api-client:
generate-radix-api-client: bootstrap
swagger generate client -t ./models/radix_api/generated_client -f https://api.radix.equinor.com/swaggerui/swagger.json -A radixapi


.PHONY: generate-radixconfig-envs
generate-radixconfig-envs:
# radix-id-vulnerability-scan-reader-<env>
AZURE_CLIENT_ID=b8fd30d4-61d0-4842-b6c1-e91ceb58db8c SQL_SERVER=sql-radix-cost-allocation-dev.database.windows.net envsubst < radixconfig.tpl.yaml > radixconfig.dev.yaml
AZURE_CLIENT_ID=bb6d92a0-2f6d-421e-80e6-1b2174953d21 SQL_SERVER=sql-radix-cost-allocation-c2-prod.database.windows.net envsubst < radixconfig.tpl.yaml > radixconfig.c2.yaml
AZURE_CLIENT_ID=ed3ef8ee-c9b2-4a89-9b0d-47b40abb2bf1 SQL_SERVER=sql-radix-cost-allocation-prod.database.windows.net envsubst < radixconfig.tpl.yaml > radixconfig.platform.yaml
AZURE_CLIENT_ID=bc4b6c73-78c2-4b22-ab08-575706a338ec SQL_SERVER=sql-radix-cost-allocation-playground.database.windows.net envsubst < radixconfig.tpl.yaml > radixconfig.playground.yaml


# This make command is only needed for local testing now
# we also do make swagger inside Dockerfile
.PHONY: swagger
swagger:
rm -f ./swaggerui/html/swagger.json
swagger generate spec -o ./swagger.json --scan-models --exclude-deps --exclude=github.com/equinor/radix-cost-allocation-api/models/radix_api/generated_client/models
swagger validate ./swagger.json && \
mv swagger.json ./swaggerui/html/swagger.json
swagger: bootstrap
swagger generate spec -o ./swaggerui/html/swagger.json --scan-models --exclude-deps --exclude=github.com/equinor/radix-cost-allocation-api/models/radix_api/generated_client/models
swagger validate ./swaggerui/html/swagger.json

.PHONY: mocks
mocks:
mocks: bootstrap
mockgen -source ./repository/repository.go -destination ./repository/mock/repository_mock.go -package mock
mockgen -source ./models/radix_api/client.go -destination ./api/test/mock/radix_api_client_mock.go -package mock
mockgen -source ./api/utils/auth/auth_provider.go -destination ./api/test/mock/auth_provider_mock.go -package mock
mockgen -source ./service/costservice.go -destination ./service/mock/costservice.go -package mock

HAS_SWAGGER := $(shell command -v swagger;)
HAS_GOLANGCI_LINT := $(shell command -v golangci-lint;)
HAS_MOCKGEN := $(shell command -v mockgen;)

bootstrap:
ifndef HAS_SWAGGER
go install github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5
endif
ifndef HAS_GOLANGCI_LINT
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
endif
ifndef HAS_MOCKGEN
go install github.com/golang/mock/mockgen@v1.6.0
endif
24 changes: 12 additions & 12 deletions api/cost/cost_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (
"time"

"github.com/equinor/radix-common/models"
radixhttp "github.com/equinor/radix-common/net/http"
"github.com/equinor/radix-common/utils"
internalutils "github.com/equinor/radix-cost-allocation-api/api/internal/utils"
"github.com/equinor/radix-cost-allocation-api/models/radix_api"
"github.com/equinor/radix-cost-allocation-api/service"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/rs/zerolog"
)

const rootPath = ""
Expand Down Expand Up @@ -172,34 +172,34 @@ func (costController *costController) GetFutureCost(accounts models.Accounts, w

func (costController *costController) getFutureCost(accounts models.Accounts, w http.ResponseWriter, r *http.Request, appName string) {
handler := NewCostHandler(accounts, costController.radixapi, costController.costService)
cost, err := handler.GetFutureCost(appName)
cost, err := handler.GetFutureCost(r.Context(), appName)

if err != nil {
log.Errorf("failed to get future cost. Error: %v", err)
radixhttp.ErrorResponseForServer(w, r, fmt.Errorf("failed to get future cost"))
zerolog.Ctx(r.Context()).Error().Err(err).Msg("failed to get future cost")
internalutils.ErrorResponseForServer(w, r, fmt.Errorf("failed to get future cost"))
return
}

radixhttp.JSONResponse(w, r, &cost)
internalutils.JSONResponse(w, r, &cost)
}

func (costController *costController) getTotalCosts(accounts models.Accounts, w http.ResponseWriter, r *http.Request, appName *string) {
fromTime, toTime, err := getCostPeriod(r)
if err != nil {
log.Errorf("failed to get total cost period. Error: %v", err)
radixhttp.ErrorResponseForServer(w, r, fmt.Errorf("failed to get total cost period"))
zerolog.Ctx(r.Context()).Error().Err(err).Msg("failed to get total cost period")
internalutils.ErrorResponseForServer(w, r, fmt.Errorf("failed to get total cost period"))
return
}

handler := NewCostHandler(accounts, costController.radixapi, costController.costService)
cost, err := handler.GetTotalCost(fromTime, toTime, appName)
cost, err := handler.GetTotalCost(r.Context(), fromTime, toTime, appName)
if err != nil {
log.Errorf("failed to get total cost. Error: %v", err)
radixhttp.ErrorResponseForServer(w, r, fmt.Errorf("failed to get total cost"))
zerolog.Ctx(r.Context()).Error().Err(err).Msg("failed to get total cost")
internalutils.ErrorResponseForServer(w, r, fmt.Errorf("failed to get total cost"))
return
}

radixhttp.JSONResponse(w, r, cost)
internalutils.JSONResponse(w, r, cost)
}

func getCostPeriod(r *http.Request) (*time.Time, *time.Time, error) {
Expand Down
7 changes: 4 additions & 3 deletions api/cost/cost_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ type controllerTestSuite struct {
}

func (s *controllerTestSuite) SetupTest() {
os.Setenv("WHITELIST", "{\"whiteList\": [\"canarycicd-test\",\"canarycicd-test1\",\"canarycicd-test2\",\"canarycicd-test3\",\"canarycicd-test4\",\"radix-api\",\"radix-canary-golang\",\"radix-cost-allocation-api\",\"radix-github-webhook\",\"radix-platform\",\"radix-web-console\"]}")
s.env = models.NewEnv()
_ = os.Setenv("WHITELIST", "{\"whiteList\": [\"canarycicd-test\",\"canarycicd-test1\",\"canarycicd-test2\",\"canarycicd-test3\",\"canarycicd-test4\",\"radix-api\",\"radix-canary-golang\",\"radix-cost-allocation-api\",\"radix-github-webhook\",\"radix-platform\",\"radix-web-console\"]}")
s.env, _, _ = models.NewEnv()
ctrl := gomock.NewController(s.T())
s.authProvider = mock.NewMockAuthProvider(ctrl)
s.idToken = mock.NewMockIDToken(ctrl)
Expand Down Expand Up @@ -107,7 +107,8 @@ func (s *controllerTestSuite) Test_TotalCost_ApplicationExist() {
response := controllerTestUtils.ExecuteRequest("GET", url)

applicationCostSet := models.ApplicationCostSet{}
controllertest.GetResponseBody(response, &applicationCostSet)
err := controllertest.GetResponseBody(response, &applicationCostSet)
s.Require().NoError(err)
s.NotNil(applicationCostSet)
s.Equal(expected.ApplicationCosts[0], applicationCostSet.ApplicationCosts[0])
}
Expand Down
13 changes: 7 additions & 6 deletions api/cost/cost_handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cost

import (
"context"
"fmt"
"time"

Expand All @@ -12,7 +13,7 @@ import (
"github.com/equinor/radix-cost-allocation-api/models/radix_api/generated_client/client/platform"
"github.com/equinor/radix-cost-allocation-api/service"
_ "github.com/microsoft/go-mssqldb"
log "github.com/sirupsen/logrus"
"github.com/rs/zerolog"
)

// CostHandler Instance variables
Expand All @@ -36,7 +37,7 @@ func (costHandler *CostHandler) getToken() string {
}

// GetTotalCost handler for GetTotalCost
func (costHandler *CostHandler) GetTotalCost(fromTime, toTime *time.Time, appName *string) (*models.ApplicationCostSet, error) {
func (costHandler *CostHandler) GetTotalCost(ctx context.Context, fromTime, toTime *time.Time, appName *string) (*models.ApplicationCostSet, error) {
applicationCostSet, err := costHandler.costService.GetCostForPeriod(*fromTime, *toTime)
if err != nil {
return nil, err
Expand All @@ -49,7 +50,7 @@ func (costHandler *CostHandler) GetTotalCost(fromTime, toTime *time.Time, appNam
rrMap, err := costHandler.getRadixRegistrationMap(appName)

if err != nil {
log.Info("Could not get application details. ", err)
zerolog.Ctx(ctx).Info().Err(err).Msg("Could not get application details")
return nil, err
}

Expand All @@ -60,7 +61,7 @@ func (costHandler *CostHandler) GetTotalCost(fromTime, toTime *time.Time, appNam
}

// GetFutureCost estimates cost for the next 30 days based on last run
func (costHandler *CostHandler) GetFutureCost(appName string) (*models.ApplicationCost, error) {
func (costHandler *CostHandler) GetFutureCost(ctx context.Context, appName string) (*models.ApplicationCost, error) {
cost, err := costHandler.costService.GetFutureCost(appName)
if err != nil {
return nil, err
Expand All @@ -69,7 +70,7 @@ func (costHandler *CostHandler) GetFutureCost(appName string) (*models.Applicati
rrMap, err := costHandler.getRadixRegistrationMap(&appName)

if err != nil {
log.Debugf("Unable to get application details. Error: %v", err)
zerolog.Ctx(ctx).Debug().Err(err).Msg("Unable to get application details")
return nil, err
}

Expand All @@ -80,7 +81,7 @@ func (costHandler *CostHandler) GetFutureCost(appName string) (*models.Applicati
}

err = fmt.Errorf("user does not have access to application %s", appName)
log.Debugf("Error: %s", err.Error())
zerolog.Ctx(ctx).Debug().Msgf(err.Error())
return nil, radixhttp.ApplicationNotFoundError("Application was not found.", err)
}

Expand Down
34 changes: 34 additions & 0 deletions api/internal/utils/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package utils

import (
"io"
"net/http"

radixhttp "github.com/equinor/radix-common/net/http"
"github.com/rs/zerolog"
)

// ErrorResponseForServer Marshals error for server requester
func ErrorResponseForServer(w http.ResponseWriter, r *http.Request, apiError error) {
err := radixhttp.ErrorResponseForServer(w, r, apiError)
if err != nil {
zerolog.Ctx(r.Context()).Error().Err(err).Msgf("%s %s: failed to write server response", r.Method, r.URL.Path)
}
}

// JSONResponse Marshals response with header
func JSONResponse(w http.ResponseWriter, r *http.Request, result interface{}) {
err := radixhttp.JSONResponse(w, r, result)
if err != nil {
zerolog.Ctx(r.Context()).Error().Err(err).Msgf("%s %s: failed to write response", r.Method, r.URL.Path)
}
}

// ReaderFileResponse writes the content from the reader to the response,
// and sets Content-Disposition=attachment; filename=<filename arg>
func ReaderFileResponse(w http.ResponseWriter, r *http.Request, reader io.Reader, fileName, contentType string) {
err := radixhttp.ReaderFileResponse(w, reader, fileName, contentType)
if err != nil {
zerolog.Ctx(r.Context()).Error().Err(err).Msgf("%s %s: failed to write response", r.Method, r.URL.Path)
}
}
2 changes: 1 addition & 1 deletion api/report/models/cost_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (cr *CostReport) Create(out io.Writer) error {
csvWriter := csv.NewWriter(writer)
// Set field seperator to ;
csvWriter.Comma = ';'
defer writer.Flush()
defer func() { _ = writer.Flush() }()

err := csvWriter.Write(columns)

Expand Down
10 changes: 5 additions & 5 deletions api/report/report_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"time"

"github.com/equinor/radix-common/models"
radixhttp "github.com/equinor/radix-common/net/http"
"github.com/equinor/radix-cost-allocation-api/api/internal/utils"
"github.com/equinor/radix-cost-allocation-api/service"
log "github.com/sirupsen/logrus"
"github.com/rs/zerolog"
)

const rootPath = ""
Expand Down Expand Up @@ -57,11 +57,11 @@ func (rc *reportController) GetCostReport(_ models.Accounts, w http.ResponseWrit

err := handler.GetCostReport(&b, fromDate, toDate)
if err != nil {
log.Errorf("Failed to get report. Error: %v", err)
radixhttp.ErrorResponseForServer(w, r, fmt.Errorf("failed to get report"))
zerolog.Ctx(r.Context()).Error().Err(err).Msg("Failed to get report")
utils.ErrorResponseForServer(w, r, fmt.Errorf("failed to get report"))
}

radixhttp.ReaderFileResponse(w, &b, fileName, "text/plain; charset=utf-8")
utils.ReaderFileResponse(w, r, &b, fileName, "text/plain; charset=utf-8")
}

// from is the first day of the previous month
Expand Down
Loading