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

chore: tidy codebase #6

Merged
merged 1 commit into from
Sep 3, 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
2 changes: 0 additions & 2 deletions .github/workflows/artifacts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ jobs:
outputs: type=docker,dest=image.tar

- name: Build and push Docker image
id: build
uses: docker/build-push-action@v6
if: ${{ inputs.publish == true }}
with:
Expand Down Expand Up @@ -96,7 +95,6 @@ jobs:
run: make lint-helm

- name: Helm package
id: build
run: make helm-chart

- name: Upload Helm chart artifact
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
needs: artifacts
strategy:
matrix:
KIND_K8S_VERSION: ["v1.28.9", "v1.29.4", "v1.30.0"]
KIND_K8S_VERSION: ["v1.29.0", "v1.30.0", "v1.31.0"]

steps:
- name: Checkout repository
Expand Down
20 changes: 11 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export PATH := $(abspath bin/):${PATH}

CONTAINER_IMAGE_REF = ghcr.io/csatib02/kube-pod-autocomplete:dev
PROJECT_NAME = kube-pod-autocomplete
CONTAINER_IMAGE_REF = ghcr.io/csatib02/${PROJECT_NAME}:dev

##@ General

Expand All @@ -16,16 +17,17 @@ help: ## Display this help

.PHONY: up
up: ## Start development environment
${KIND_BIN} create cluster --name kube-pod-autocomplete
${KIND_BIN} create cluster --name ${PROJECT_NAME}

.PHONY: down
down: ## Stop development environment
${KIND_BIN} delete cluster --name kube-pod-autocomplete
${KIND_BIN} delete cluster --name ${PROJECT_NAME}

.PHONY: deploy
deploy: ## Deploy kube-pod-autocomplete to the development environment
kubectl create ns kube-pod-autocomplete
${HELM_BIN} upgrade --install kube-pod-autocomplete deploy/charts/kube-pod-autocomplete --namespace kube-pod-autocomplete
deploy: container-image ## Deploy kube-pod-autocomplete to the development environment
kind load docker-image ${CONTAINER_IMAGE_REF} --name ${PROJECT_NAME}
kubectl create ns ${PROJECT_NAME}
${HELM_BIN} upgrade --install ${PROJECT_NAME} deploy/charts/${PROJECT_NAME} --namespace ${PROJECT_NAME} --set image.tag=dev

.PHONY: deploy-testdata
deploy-testdata: ## Deploy testdata to the development environment
Expand All @@ -38,7 +40,7 @@ deploy-testdata: ## Deploy testdata to the development environment
.PHONY: build
build: ## Build binary
@mkdir -p build
go build -race -o build/kube-pod-autocomplete .
go build -race -o build/${PROJECT_NAME} .

.PHONY: artifacts
artifacts: container-image helm-chart
Expand All @@ -51,7 +53,7 @@ container-image: ## Build container image
.PHONY: helm-chart
helm-chart: ## Build Helm chart
@mkdir -p build
$(HELM_BIN) package -d build/ deploy/charts/kube-pod-autocomplete
$(HELM_BIN) package -d build/ deploy/charts/${PROJECT_NAME}

##@ Checks

Expand Down Expand Up @@ -79,7 +81,7 @@ lint-go:

.PHONY: lint-helm
lint-helm:
$(HELM_BIN) lint deploy/charts/kube-pod-autocomplete
$(HELM_BIN) lint deploy/charts/${PROJECT_NAME}

.PHONY: fmt
fmt: ## Format code
Expand Down
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,8 @@
- Kube Pod Autocomplete is designed to be used in Kubernetes environment.
- Take a look at the [documentation](./docs/docs.md).

## TODO

- Consider adding garden config to simplify testing.

- Add search pods by label/ns/phase endpoint as a possible use-case.

## Development

Make sure Docker is installed.

Fetch required tools:

```shell
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ spec:
containers:
- name: {{ .Values.name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.containerPort }}
- containerPort: {{ .Values.service.containerPort }}
resources:
limits:
cpu: {{ .Values.resources.limits.cpu }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ spec:
ports:
- protocol: TCP
port: {{ .Values.service.servicePort }}
targetPort: {{ .Values.containerPort }}
externalPort: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.containerPort }}
3 changes: 1 addition & 2 deletions deploy/charts/kube-pod-autocomplete/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace: kube-pod-autocomplete
# General settings for the application
name: kube-pod-autocomplete
replicas: 1
containerPort: 8080

# image
image:
Expand All @@ -17,7 +16,7 @@ service:
name: kube-pod-autocomplete-service
type: ClusterIP
servicePort: 8080
externalPort: 8080
containerPort: 8080

# Service Account settings
serviceAccount:
Expand Down
4 changes: 3 additions & 1 deletion docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ make deploy-testdata
Port-forward to Kube Pod Autocomplete:

```shell
kubectl port-forward -n kube-pod-autocomplete $(kubectl get pods -n kube-pod-autocomplete -o jsonpath='{.items[0].metadata.name}') 8080:8080 1>/dev/null &
kubectl port-forward -n kube-pod-autocomplete "svc/$(kubectl get svc -n kube-pod-autocomplete -o jsonpath='{.items[0].metadata.name}')" 8080:8080 1>/dev/null &
```

Hit the endpoint:
Expand All @@ -50,3 +50,5 @@ curl -X GET http://localhost:8080/search/autocomplete/pods
- [ ] Add support for `caching`. (While the current implementation is fast with a small number of pods, but there can be problems in production environments.)
- [ ] `Generate` API specification from `OpenAPI spec`. (The current solution is a really simple POC, if the project is later expanded with additional endpoints code generation from the OpenAPI spec should be utilised.)
- [ ] `Improve End-to-End` Tests: (The existing end-to-end test setup is quite basic, using `cmd.Exec()` and port-forward to access the service is rather limited. Future improvements could include using an ingress controller like NGINX for more robust testing.)
- [ ] Add endpoints that can be called with the received suggestions. (E.g.: `search/:resource/:filters` or get the filters from the body.)
- [ ] Enable deploying with [Garden](https://garden.io/). (Garden helps a lot when it comes to manually testing a project, as new features are added, this should also be implemented.)
3 changes: 1 addition & 2 deletions e2e/deploy/kube-pod-autocomplete/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ namespace: kube-pod-autocomplete

name: kube-pod-autocomplete
replicas: 1
containerPort: 8080

service:
name: kube-pod-autocomplete-service
type: ClusterIP
servicePort: 8080
externalPort: 8080
containerPort: 8080

serviceAccount:
name: kube-pod-autocomplete-sa
Expand Down
11 changes: 4 additions & 7 deletions e2e/kpa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const (
func TestKPAEndpoints(t *testing.T) {
endpoints := applyResource(features.New("validate endpoint functionality")).
Assess("pods are available", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
// check if test pods are running
// Check if test pods are running
err := wait.PollUntilContextTimeout(ctx, pollInterval, defaultTimeout, true, func(ctx context.Context) (bool, error) {
// get all pods with label: team=test
// Get all pods with label: team=test
pods := &corev1.PodList{}
err := cfg.Client().Resources().List(ctx, pods, func(opts *metav1.ListOptions) {
opts.LabelSelector = labels.Set{"team": "test"}.String()
Expand All @@ -53,26 +53,23 @@ func TestKPAEndpoints(t *testing.T) {
return ctx
}).Assess("check KPA response", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {

// start port forwarding
killProcess := startPortForwardingToService(t, "kube-pod-autocomplete-service", "kube-pod-autocomplete", "8080:8080")
defer killProcess()

// hit the /health endpoint
resp, err := http.Get(fmt.Sprintf(healthURL, "localhost", 8080))
require.NoError(t, err)
defer resp.Body.Close()

require.Equal(t, http.StatusOK, resp.StatusCode)

// hit the /search/autocomplete/pods endpoint
autocompleteUrl := fmt.Sprintf(autocompleteURL, "localhost", 8080, "pods")
resp, err = http.Get(autocompleteUrl)
require.NoError(t, err)
defer resp.Body.Close()

require.Equal(t, http.StatusOK, resp.StatusCode)

// check if the response body contains the expected filters
// Check if the response body contains the expected filters
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)

Expand Down Expand Up @@ -112,7 +109,7 @@ func applyResource(builder *features.FeatureBuilder) *features.FeatureBuilder {
func startPortForwardingToService(t *testing.T, svcName, ns, portMapping string) func() {
args := []string{"port-forward", fmt.Sprintf("svc/%s", svcName), portMapping, "-n", ns}
cmd := exec.Command("kubectl", args...)
cmd.Stderr = os.Stderr // redirect stderr to test output
cmd.Stderr = os.Stderr // Redirect stderr to test output
err := cmd.Start()
require.NoError(t, err)

Expand Down
4 changes: 1 addition & 3 deletions internal/handlers/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ func AutocompleteHandler(c *gin.Context) {
return
}

// Pretty-print the JSON response
prettyJSON, err := json.MarshalIndent(suggestions, "", " ")
if err != nil {
// Log the error and return the response as is
Expand All @@ -79,10 +78,9 @@ func AutocompleteHandler(c *gin.Context) {
return
}

c.Data(http.StatusOK, "application/json", prettyJSON)
c.Data(http.StatusOK, gin.MIMEJSON, prettyJSON)
}

// validateRequestedFilters validates the requestedFilters parameter
func validateRequestedFilters(requestedFilters []string) ([]string, error) {
validFilters := make([]string, 0, len(requestedFilters))
for _, filter := range requestedFilters {
Expand Down
9 changes: 2 additions & 7 deletions internal/services/autocomplete/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ func NewAutoCompleteService() (*Service, error) {

// GetAutocompleteSuggestions returns a list of suggestions (for the given query)
func (s *Service) GetAutocompleteSuggestions(ctx context.Context, req model.AutoCompleteRequest) (*model.AutocompleteSuggestions, error) {
// if no ResourceType is provided, default to Pod
if req.ResourceType == "" {
req.ResourceType = common.PodResourceType
}

filters, err := filter.NewFieldFilters(req.ResourceType, &req.Filters)
if err != nil {
return nil, fmt.Errorf("failed to get field filters: %w", err)
Expand All @@ -46,7 +41,7 @@ func (s *Service) GetAutocompleteSuggestions(ctx context.Context, req model.Auto
return s.extractSuggestions(resources, filters)
}

// extractSuggestions extracts suggestions from the given pods based on the requested filters
// extractSuggestions extracts suggestions from the given resources based on the requested filters
func (s *Service) extractSuggestions(resources common.Resources, filters *map[string]model.FieldFilter) (*model.AutocompleteSuggestions, error) {
suggestions := make([]model.Suggestion, 0, len(*filters))
for fieldName, fieldFilter := range *filters {
Expand Down Expand Up @@ -76,7 +71,7 @@ func (s *Service) extractSuggestions(resources common.Resources, filters *map[st
}
}

// These should be filter options on the UI
// These should be options on the UI
filterOptions := filter.Options{}

filterOptions.RemoveDuplicateValues(&suggestions)
Expand Down
1 change: 0 additions & 1 deletion internal/services/autocomplete/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ func NewFieldFilters(resourceType common.ResourceType, requestedFilters *[]strin
switch resourceType {
case common.PodResourceType:
return podfilter.GetFilters(requestedFilters), nil
// Add cases for other resource types here
default:
return nil, errors.New("unsupported resource type")
}
Expand Down
4 changes: 2 additions & 2 deletions internal/services/autocomplete/filter/filteroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/csatib02/kube-pod-autocomplete/internal/services/autocomplete/model"
)

// FilterOptions defines additional options for filtering suggestions
// Options defines additional options for filtering suggestions
type Options struct{}

var ignoredKeys = map[string][]string{
Expand All @@ -33,7 +33,7 @@ func (o *Options) RemoveDuplicateValues(suggestions *[]model.Suggestion) {
}

// RemoveIgnoredKeys removes the ignored keys from the suggestions
// NOTE: IgnoreKeys should be retrieved from request parameters
// NOTE: ignoredKeys should be retrieved from request parameters
func (o *Options) RemoveIgnoredKeys(suggestions *[]model.Suggestion) {
filteredSuggestions := make([]model.Suggestion, 0, len(*suggestions))
for _, suggestion := range *suggestions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import "github.com/csatib02/kube-pod-autocomplete/pkg/common"
type AutoCompleteRequest struct {
ResourceType common.ResourceType `json:"resourceType"`
Filters []string `json:"filters"`
Query string `json:"query"` // Currently not used
Query string `json:"query"` // Currently unused
}
5 changes: 1 addition & 4 deletions internal/services/autocomplete/model/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package model

import "github.com/csatib02/kube-pod-autocomplete/pkg/common"

// FieldExtractor interface defines the method for extracting field values from a PodList
// NOTE: There is no actual difference between ListExtractor and MapExtractor,
// since when processing the extracted data, we can always check the type of the underlying data structure
// via FieldFilter.Type, but for the sake of clarity, I have defined two separate types.
// FieldExtractor interface defines the method for extracting field values from resources
type FieldExtractor interface {
Extract(common.Resources) any
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func (e *Error) Error() string {
return e.Message
}

// HandleHTTPError is a utility function to handle HTTP errors in Gin handlers.
// HandleHTTPError is a utility function to handle HTTP errors in Gin handlers
func HandleHTTPError(c *gin.Context, code int, err error) {
httpErr := &Error{
Code: code,
Expand Down
Loading