From 52aebc5b36d45a28594650d2c7ec77c1671b3a27 Mon Sep 17 00:00:00 2001 From: hellices Date: Sat, 31 May 2025 22:33:44 +0900 Subject: [PATCH 01/27] update readme --- README.md | 2 -- internal/controller/openapiaggregator_controller.go | 11 ++++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cf77c2a..f46653f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/hellices/openapi-aggregator-operator)](https://goreportcard.com/report/github.com/hellices/openapi-aggregator-operator) [![GitHub License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![Go Version](https://img.shields.io/github/go-mod/go-version/hellices/openapi-aggregator-operator)](go.mod) -[![Docker Pulls](https://img.shields.io/docker/pulls/hellices/openapi-aggregator-operator)](https://hub.docker.com/r/hellices/openapi-aggregator-operator) -[![Release](https://img.shields.io/github/v/release/hellices/openapi-aggregator-operator)](https://github.com/hellices/openapi-aggregator-operator/releases) Kubernetes operator that discovers and aggregates OpenAPI/Swagger specifications from services running in your cluster. It provides a unified Swagger UI interface to browse and test all your APIs in one place. diff --git a/internal/controller/openapiaggregator_controller.go b/internal/controller/openapiaggregator_controller.go index 704e31e..8c11b62 100644 --- a/internal/controller/openapiaggregator_controller.go +++ b/internal/controller/openapiaggregator_controller.go @@ -80,14 +80,15 @@ func (r *OpenAPIAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Re logger.Info("Processing service", "name", service.Name, "namespace", service.Namespace) if apiInfo := r.processService(ctx, service, instance); apiInfo != nil { - logger.Info("Successfully collected API info", "service", service.Name, "url", apiInfo.URL) + logger.Info("Collected API info", "service", service.Name, "url", apiInfo.URL) collectedAPIs = append(collectedAPIs, *apiInfo) } else { - logger.Info("Service skipped", "name", service.Name, "namespace", service.Namespace) + logger.V(1).Info("Service skipped", "name", service.Name, "namespace", service.Namespace) } } - // Update status + // Simply update the status with the new list + // Any removed services will naturally be excluded instance.Status.CollectedAPIs = collectedAPIs if err := r.Status().Update(ctx, instance); err != nil { logger.Error(err, "Failed to update OpenAPIAggregator status") @@ -183,8 +184,8 @@ func (r *OpenAPIAggregatorReconciler) checkAPIHealth(ctx context.Context, apiInf // SetupWithManager sets up the controller with the Manager. func (r *OpenAPIAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) error { // Initialize Swagger UI server - r.swaggerServer = swagger.NewServer() - if !r.TestMode { + if r.swaggerServer == nil && !r.TestMode { + r.swaggerServer = swagger.NewServer() go func() { log.Log.Info("Starting Swagger UI server on HTTP port 9090") if err := r.swaggerServer.Start(9090); err != nil { From ecb0908dca743f88f0a24f1130c8bb270151177f Mon Sep 17 00:00:00 2001 From: hellices Date: Sat, 31 May 2025 23:12:32 +0900 Subject: [PATCH 02/27] Improve OpenAPI UI and service handling 1. Enhanced UI: - Added namespace filtering with 'All Namespaces' option - Implemented real-time filtering in selection boxes - Improved service information display - Added side-by-side namespace and service selectors 2. Backend improvements: - Added JSON tags to APIMetadata struct - Removed debug logging - Fixed service name parsing for API loading - Improved error handling in spec fetching --- .../openapiaggregator_controller.go | 10 +- pkg/swagger/server.go | 31 +-- pkg/swagger/swagger-ui/index.html | 224 +++++++++++++++--- 3 files changed, 212 insertions(+), 53 deletions(-) diff --git a/internal/controller/openapiaggregator_controller.go b/internal/controller/openapiaggregator_controller.go index 8c11b62..b7eef08 100644 --- a/internal/controller/openapiaggregator_controller.go +++ b/internal/controller/openapiaggregator_controller.go @@ -139,14 +139,12 @@ func (r *OpenAPIAggregatorReconciler) processService(ctx context.Context, svc co Namespace: svc.Namespace, Path: path, Port: port, - // URL: fmt.Sprintf("http://%s.%s.svc.cluster.local:%s%s", svc.Name, svc.Namespace, port, path), - URL: fmt.Sprintf("http://%s:%s%s", svc.Spec.ClusterIP, port, path), - LastUpdated: time.Now().Format(time.RFC3339), - Annotations: svc.Annotations, + URL: fmt.Sprintf("http://%s.%s.svc.cluster.local:%s%s", svc.Name, svc.Namespace, port, path), + LastUpdated: time.Now().Format(time.RFC3339), + Annotations: svc.Annotations, } - // Check if the OpenAPI endpoint is accessible - + // Enable health check to validate accessibility // TODO: Uncomment this line for production use // r.checkAPIHealth(ctx, apiInfo) diff --git a/pkg/swagger/server.go b/pkg/swagger/server.go index 9b64b2e..e1c2431 100644 --- a/pkg/swagger/server.go +++ b/pkg/swagger/server.go @@ -16,11 +16,15 @@ import ( var swaggerUI embed.FS type APIMetadata struct { - Name string - URL string - Title string - Version string - Description string + Name string `json:"name"` + URL string `json:"url"` + Title string `json:"title"` + Version string `json:"version"` + Description string `json:"description"` + ResourceType string `json:"resourceType"` + ResourceName string `json:"resourceName"` + Namespace string `json:"namespace"` + LastUpdated string `json:"lastUpdated"` } // Server serves the Swagger UI and aggregated OpenAPI specs @@ -41,25 +45,24 @@ func (s *Server) UpdateSpecs(apis []observabilityv1alpha1.APIInfo) { s.specsMux.Lock() defer s.specsMux.Unlock() - fmt.Printf("Updating specs with %d APIs\n", len(apis)) - newSpecs := make(map[string]APIMetadata) for _, api := range apis { - fmt.Printf("Processing API %s (URL: %s, Error: %s)\n", api.Name, api.URL, api.Error) if api.Error != "" { continue } - // Store only metadata metadata := APIMetadata{ - Name: api.Name, - URL: api.URL, - Title: api.Name, - Description: fmt.Sprintf("API from %s/%s", api.Namespace, api.ResourceName), + Name: api.Name, + URL: api.URL, + Title: api.Name, + Description: fmt.Sprintf("API from %s/%s", api.Namespace, api.ResourceName), + ResourceType: api.ResourceType, + ResourceName: api.ResourceName, + Namespace: api.Namespace, + LastUpdated: api.LastUpdated, } newSpecs[api.Name] = metadata - fmt.Printf("Added metadata for %s\n", api.Name) } fmt.Printf("Total APIs processed: %d\n", len(newSpecs)) s.specs = newSpecs diff --git a/pkg/swagger/swagger-ui/index.html b/pkg/swagger/swagger-ui/index.html index 5f17474..41dd2f3 100644 --- a/pkg/swagger/swagger-ui/index.html +++ b/pkg/swagger/swagger-ui/index.html @@ -47,7 +47,25 @@ margin-bottom: 20px; font-family: sans-serif; } - .api-selector select { + .selector-container { + display: flex; + gap: 20px; + margin-bottom: 15px; + } + .selector-group { + flex: 1; + } + .selector-group label { + display: block; + color: rgba(255,255,255,0.7); + margin-bottom: 5px; + font-size: 12px; + font-family: sans-serif; + } + .api-selector .select-container { + position: relative; + } + .api-selector .select-container input { width: 100%; padding: 10px 32px 10px 12px; border: 1px solid #50555e; @@ -56,14 +74,20 @@ color: #fff; font-family: sans-serif; font-size: 14px; - appearance: none; - -webkit-appearance: none; - -moz-appearance: none; background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 10px center; background-size: 20px; } + .api-selector .select-container input:hover { + border-color: #89bf04; + background-color: #222; + } + .api-selector .select-container input:focus { + outline: none; + border-color: #89bf04; + box-shadow: 0 0 0 1px #89bf04; + } .api-selector select:hover { border-color: #89bf04; background-color: #222; @@ -97,10 +121,26 @@

OpenAPI Specifications

-
Select an API service to view its documentation
- +
Select a namespace and service to view its documentation
+
+
+ +
+ + + +
+
+
+ +
+ + + +
+
+
@@ -110,26 +150,116 @@

OpenAPI Specifications

From ea8aa011bbe503dcd453d1b7ca280a2e5c44bed3 Mon Sep 17 00:00:00 2001 From: hellices Date: Sun, 1 Jun 2025 00:37:19 +0900 Subject: [PATCH 04/27] =?UTF-8?q?=EB=A1=9C=EC=BB=AC=20=EA=B0=9C=EB=B0=9C?= =?UTF-8?q?=ED=99=98=EA=B2=BD=20=ED=8E=B8=EC=9D=98=20=EA=B0=9C=EC=84=A0?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- README.md | 14 +++++++++++++- pkg/swagger/server.go | 15 +++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 7e65181..6167562 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ build: manifests generate fmt vet ## Build manager binary. .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - go run ./cmd/main.go + DEV_MODE=true go run ./cmd/main.go # If you wish to build the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. diff --git a/README.md b/README.md index f46653f..43fcddd 100644 --- a/README.md +++ b/README.md @@ -120,8 +120,18 @@ make deploy For installation via Operator Lifecycle Manager, see detailed instructions in [OLM Installation Guide](docs/olm-install.md). -### Development Commands +### Development Environment Setup +1. Deploy the test service: +```bash +# First, deploy the test service that provides OpenAPI specs +kubectl apply -f config/samples/test-service.yaml + +# Port forward the test service to localhost:8080 +kubectl port-forward svc/test-service 8080:8080 +``` + +2. Run the operator in development mode: ```bash # Run locally make run @@ -136,6 +146,8 @@ make docker-build docker-push make manifests ``` +Note: When running the operator in development mode with `make run`, ensure that the test service is running and port-forwarded to localhost:8080. This is required for the operator to properly fetch and display the OpenAPI specifications in the Swagger UI. + ### Version Management - Version is managed in `versions.txt` diff --git a/pkg/swagger/server.go b/pkg/swagger/server.go index 5e14283..caec388 100644 --- a/pkg/swagger/server.go +++ b/pkg/swagger/server.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "os" "strings" "sync" @@ -108,10 +109,14 @@ func (s *Server) serveIndividualSpec(w http.ResponseWriter, r *http.Request) { } // Fetch the spec in real-time - resp, err := http.Get(metadata.URL) - // for test - // fmt.Printf("Metadata for %s: %+v, exists: %v\n", apiName, metadata, exists) - // resp, err := http.Get("https://petstore.swagger.io/v2/swagger.json") + url := metadata.URL + if os.Getenv("DEV_MODE") == "true" { + // In development mode, rewrite any cluster URLs to localhost:8080 + if strings.Contains(url, ".svc.cluster.local:8080") { + url = "http://localhost:8080" + strings.Split(url, ".svc.cluster.local:8080")[1] + } + } + resp, err := http.Get(url) if err != nil { http.Error(w, fmt.Sprintf("Failed to fetch spec: %v", err), http.StatusInternalServerError) return @@ -192,6 +197,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +//TODO: swagger에서 보내는 요청을 모두 server.go로 리다이렉트하는 기능 추가. 서버는 이걸 받아서 proxy 요청을 보내는 기능을 구현해야 함. + // Start starts the Swagger UI server func (s *Server) Start(port int) error { // Use the embedded file system instead of serving from disk From ec77782f5c12bef2d93b2af03689c5980427d057 Mon Sep 17 00:00:00 2001 From: hellices Date: Sun, 1 Jun 2025 00:56:06 +0900 Subject: [PATCH 05/27] for ingress config --- README.md | 49 +++++++++++++++++++++++++++++++ pkg/swagger/server.go | 45 +++++++++++++++++++++------- pkg/swagger/swagger-ui/index.html | 8 ++++- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 43fcddd..4c3e563 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,55 @@ Then open http://localhost:9090 in your browser. - 📝 **Service Information**: Displays service metadata including namespace and resource type - ⚡ **Zero-config Services**: Works with any service that exposes an OpenAPI/Swagger specification +### 5. Ingress/Route Integration + +You can expose the Swagger UI through Ingress or OpenShift Route. + +#### Using Kubernetes Ingress + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: swagger-ui + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + rules: + - host: api.example.com + http: + paths: + - path: /swagger-ui(/|$)(.*) + pathType: Prefix + backend: + service: + name: openapi-aggregator-openapi-aggregator-swagger-ui + port: + number: 9090 +``` + +And set the environment variable in the deployment: +```yaml +env: +- name: SWAGGER_BASE_PATH + value: /swagger-ui +``` + +#### Using OpenShift Route + +```yaml +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: swagger-ui +spec: + to: + kind: Service + name: openapi-aggregator-openapi-aggregator-swagger-ui + port: + targetPort: swagger-ui +``` + ## Project Architecture ### Components diff --git a/pkg/swagger/server.go b/pkg/swagger/server.go index caec388..51c5c76 100644 --- a/pkg/swagger/server.go +++ b/pkg/swagger/server.go @@ -34,12 +34,14 @@ type APIMetadata struct { type Server struct { specs map[string]APIMetadata // Map of API name to metadata specsMux sync.RWMutex // Mutex for thread-safe access to specs + basePath string // Base path for the server (for Ingress/Route support) } // NewServer creates a new Swagger UI server func NewServer() *Server { return &Server{ - specs: make(map[string]APIMetadata), + specs: make(map[string]APIMetadata), + basePath: os.Getenv("SWAGGER_BASE_PATH"), } } @@ -71,6 +73,14 @@ func (s *Server) UpdateSpecs(apis []observabilityv1alpha1.APIInfo) { s.specs = newSpecs } +// stripBasePath removes the base path prefix from the request path +func (s *Server) stripBasePath(path string) string { + if s.basePath != "" && strings.HasPrefix(path, s.basePath) { + return strings.TrimPrefix(path, s.basePath) + } + return path +} + // serveIndex serves the Swagger UI index page func (s *Server) serveIndex(w http.ResponseWriter, r *http.Request) { indexContent, err := swaggerUI.ReadFile("swagger-ui/index.html") @@ -78,8 +88,14 @@ func (s *Server) serveIndex(w http.ResponseWriter, r *http.Request) { http.Error(w, "Failed to read index.html", http.StatusInternalServerError) return } + + // Add base path meta tag + htmlContent := string(indexContent) + metaTag := fmt.Sprintf(``, s.basePath) + htmlContent = strings.Replace(htmlContent, "", metaTag+"", 1) + w.Header().Set("Content-Type", "text/html") - if _, err := w.Write(indexContent); err != nil { + if _, err := w.Write([]byte(htmlContent)); err != nil { http.Error(w, "Failed to write response", http.StatusInternalServerError) } } @@ -136,11 +152,13 @@ func (s *Server) serveIndividualSpec(w http.ResponseWriter, r *http.Request) { // serveStaticFiles serves embedded static files func (s *Server) serveStaticFiles(w http.ResponseWriter, r *http.Request) { + path := s.stripBasePath(r.URL.Path) + // First try assets subdirectory for static files - content, err := swaggerUI.ReadFile("swagger-ui/assets" + r.URL.Path) + content, err := swaggerUI.ReadFile("swagger-ui/assets" + path) if err != nil { // If not found in assets, try the root swagger-ui directory - content, err = swaggerUI.ReadFile("swagger-ui" + r.URL.Path) + content, err = swaggerUI.ReadFile("swagger-ui" + path) if err != nil { http.NotFound(w, r) return @@ -148,8 +166,9 @@ func (s *Server) serveStaticFiles(w http.ResponseWriter, r *http.Request) { } // Set appropriate content type based on file extension - contentType := s.getContentType(r.URL.Path) + contentType := s.getContentType(path) w.Header().Set("Content-Type", contentType) + w.Header().Set("Cache-Control", "public, max-age=3600") // Add caching for static files if _, err := w.Write(content); err != nil { http.Error(w, "Failed to write response", http.StatusInternalServerError) @@ -173,10 +192,11 @@ func (s *Server) getContentType(path string) string { } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Set common headers + // Set common headers with more permissive CORS w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + w.Header().Set("Access-Control-Expose-Headers", "Content-Length") // Handle OPTIONS requests for CORS if r.Method == "OPTIONS" { @@ -184,13 +204,16 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + // Strip base path if configured + path := s.stripBasePath(r.URL.Path) + // Route to appropriate handler switch { - case r.URL.Path == "/" || r.URL.Path == "/index.html": + case path == "/" || path == "/index.html": s.serveIndex(w, r) - case r.URL.Path == "/swagger-specs": + case path == "/swagger-specs": s.serveSpecs(w, r) - case strings.HasPrefix(r.URL.Path, "/swagger-specs/"): + case strings.HasPrefix(path, "/api/"): s.serveIndividualSpec(w, r) default: s.serveStaticFiles(w, r) diff --git a/pkg/swagger/swagger-ui/index.html b/pkg/swagger/swagger-ui/index.html index bc46ab3..54cf202 100644 --- a/pkg/swagger/swagger-ui/index.html +++ b/pkg/swagger/swagger-ui/index.html @@ -60,6 +60,11 @@

OpenAPI Specifications

// API Data Management const apiManager = { + getBasePath() { + // Get base path from server or default to '' + return document.querySelector('meta[name="base-path"]')?.getAttribute('content') || ''; + }, + groupApisByNamespace(specs) { state.currentApisByNamespace = {}; Object.keys(specs).forEach(name => { @@ -239,7 +244,8 @@

Failed to load API specifications

Date: Sun, 1 Jun 2025 01:33:42 +0900 Subject: [PATCH 06/27] refactor: Clean up controller logging and improve verbosity levels 1. Remove unnecessary service processing logs 2. Adjust log levels for better verbosity control: - Normal operation logs to V(1) - Detailed debug info to V(2) 3. Simplify log messages for better readability 4. Remove duplicate logging in service processing --- api/v1alpha1/openapiaggregator_types.go | 7 ++ api/v1alpha1/zz_generated.deepcopy.go | 5 ++ ...lity.aggregator.io_openapiaggregators.yaml | 11 ++++ ...ervability_v1alpha1_openapiaggregator.yaml | 1 + config/samples/test-service.yaml | 1 + .../openapiaggregator_controller.go | 64 ++++++++++--------- pkg/swagger/server.go | 36 ++++++----- pkg/swagger/swagger-ui/index.html | 5 ++ 8 files changed, 83 insertions(+), 47 deletions(-) diff --git a/api/v1alpha1/openapiaggregator_types.go b/api/v1alpha1/openapiaggregator_types.go index 5ba2d03..31c1f54 100644 --- a/api/v1alpha1/openapiaggregator_types.go +++ b/api/v1alpha1/openapiaggregator_types.go @@ -44,6 +44,10 @@ type OpenAPIAggregatorSpec struct { // PortAnnotation is the annotation key for OpenAPI port // +kubebuilder:default="openapi.aggregator.io/port" PortAnnotation string `json:"portAnnotation,omitempty"` + + // AllowedMethodsAnnotation is the annotation key for allowed HTTP methods in Swagger UI + // +kubebuilder:default="openapi.aggregator.io/allowed-methods" + AllowedMethodsAnnotation string `json:"allowedMethodsAnnotation,omitempty"` } // OpenAPIAggregatorStatus defines the observed state of OpenAPIAggregator @@ -83,6 +87,9 @@ type APIInfo struct { // Annotations stores relevant annotations from the resource Annotations map[string]string `json:"annotations,omitempty"` + + // AllowedMethods stores the allowed HTTP methods for Swagger UI + AllowedMethods []string `json:"allowedMethods,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 6477834..551819c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -34,6 +34,11 @@ func (in *APIInfo) DeepCopyInto(out *APIInfo) { (*out)[key] = val } } + if in.AllowedMethods != nil { + in, out := &in.AllowedMethods, &out.AllowedMethods + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIInfo. diff --git a/config/crd/bases/observability.aggregator.io_openapiaggregators.yaml b/config/crd/bases/observability.aggregator.io_openapiaggregators.yaml index 4549c41..803eca4 100644 --- a/config/crd/bases/observability.aggregator.io_openapiaggregators.yaml +++ b/config/crd/bases/observability.aggregator.io_openapiaggregators.yaml @@ -39,6 +39,11 @@ spec: spec: description: OpenAPIAggregatorSpec defines the desired state of OpenAPIAggregator properties: + allowedMethodsAnnotation: + default: openapi.aggregator.io/allowed-methods + description: AllowedMethodsAnnotation is the annotation key for allowed + HTTP methods in Swagger UI + type: string defaultPath: default: /v2/api-docs description: DefaultPath is the default path for OpenAPI documentation @@ -77,6 +82,12 @@ spec: description: APIInfo contains information about a collected OpenAPI spec properties: + allowedMethods: + description: AllowedMethods stores the allowed HTTP methods + for Swagger UI + items: + type: string + type: array annotations: additionalProperties: type: string diff --git a/config/samples/observability_v1alpha1_openapiaggregator.yaml b/config/samples/observability_v1alpha1_openapiaggregator.yaml index 9a9c08c..a322a1d 100644 --- a/config/samples/observability_v1alpha1_openapiaggregator.yaml +++ b/config/samples/observability_v1alpha1_openapiaggregator.yaml @@ -8,3 +8,4 @@ spec: swaggerAnnotation: "openapi.aggregator.io/swagger" pathAnnotation: "openapi.aggregator.io/path" portAnnotation: "openapi.aggregator.io/port" + allowedMethodsAnnotation: "openapi.aggregator.io/allowed-methods" diff --git a/config/samples/test-service.yaml b/config/samples/test-service.yaml index 46971e5..73e8d52 100644 --- a/config/samples/test-service.yaml +++ b/config/samples/test-service.yaml @@ -8,6 +8,7 @@ metadata: openapi.aggregator.io/swagger: "true" openapi.aggregator.io/path: "/api/swagger.json" # 옵션 openapi.aggregator.io/port: "8080" # 옵션 + openapi.aggregator.io/allowed-methods: "get" # get만 허용, 'get,post'처럼 쉼표로 구분하여 여러 메서드 허용 가능 spec: ports: - port: 8080 diff --git a/internal/controller/openapiaggregator_controller.go b/internal/controller/openapiaggregator_controller.go index b7eef08..25afd7f 100644 --- a/internal/controller/openapiaggregator_controller.go +++ b/internal/controller/openapiaggregator_controller.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "net/http" + "strings" "time" corev1 "k8s.io/api/core/v1" @@ -49,9 +50,7 @@ type OpenAPIAggregatorReconciler struct { // Reconcile handles the reconciliation loop for OpenAPIAggregator resources func (r *OpenAPIAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) - - logger.Info("Starting reconciliation", "namespace", req.Namespace, "name", req.Name) + logger := log.FromContext(ctx).V(1) // 기본 로그 레벨을 1로 설정 // Fetch the OpenAPIAggregator instance instance := &observabilityv1alpha1.OpenAPIAggregator{} @@ -66,24 +65,18 @@ func (r *OpenAPIAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Re // List all services based on label selector var services corev1.ServiceList labelSelector := client.MatchingLabels(instance.Spec.LabelSelector) - logger.Info("Listing services", "labelSelector", instance.Spec.LabelSelector) if err := r.List(ctx, &services, labelSelector); err != nil { logger.Error(err, "Failed to list services") return ctrl.Result{}, err } - logger.Info("Found services", "count", len(services.Items)) // Process each service and collect OpenAPI specs var collectedAPIs []observabilityv1alpha1.APIInfo for _, service := range services.Items { - logger.Info("Processing service", "name", service.Name, "namespace", service.Namespace) - if apiInfo := r.processService(ctx, service, instance); apiInfo != nil { - logger.Info("Collected API info", "service", service.Name, "url", apiInfo.URL) + logger.V(1).Info("Collected API info", "service", service.Name, "url", apiInfo.URL) collectedAPIs = append(collectedAPIs, *apiInfo) - } else { - logger.V(1).Info("Service skipped", "name", service.Name, "namespace", service.Namespace) } } @@ -96,10 +89,9 @@ func (r *OpenAPIAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Re } // Update Swagger UI with collected specs - logger.Info("Updating Swagger UI specs", "count", len(collectedAPIs)) r.swaggerServer.UpdateSpecs(collectedAPIs) - logger.Info("Reconciliation completed", "collectedAPIs", len(collectedAPIs)) + logger.V(1).Info("Reconciliation completed", "collectedAPIs", len(collectedAPIs)) // Requeue after 10 seconds return ctrl.Result{RequeueAfter: time.Second * 10}, nil @@ -121,27 +113,42 @@ func (r *OpenAPIAggregatorReconciler) processService(ctx context.Context, svc co // Get path and port from annotations or defaults path := svc.Annotations[instance.Spec.PathAnnotation] if path == "" { - logger.Info("Using default path", "service", svc.Name, "defaultPath", instance.Spec.DefaultPath) path = instance.Spec.DefaultPath } port := svc.Annotations[instance.Spec.PortAnnotation] if port == "" { - logger.Info("Using default port", "service", svc.Name, "defaultPort", instance.Spec.DefaultPort) port = instance.Spec.DefaultPort } + // Process allowed methods + allowedMethods := make([]string, 0) + methodsStr := svc.Annotations[instance.Spec.AllowedMethodsAnnotation] + + if methodsStr != "" { + // Split the string by comma and trim spaces + for _, method := range strings.Split(methodsStr, ",") { + method = strings.ToLower(strings.TrimSpace(method)) + // Validate method + switch method { + case "get", "put", "post", "delete", "options", "head", "patch", "trace": + allowedMethods = append(allowedMethods, method) + } + } + } + // Create API info apiInfo := &observabilityv1alpha1.APIInfo{ - Name: svc.Name, - ResourceName: svc.Name, - ResourceType: "Service", - Namespace: svc.Namespace, - Path: path, - Port: port, - URL: fmt.Sprintf("http://%s.%s.svc.cluster.local:%s%s", svc.Name, svc.Namespace, port, path), - LastUpdated: time.Now().Format(time.RFC3339), - Annotations: svc.Annotations, + Name: svc.Name, + ResourceName: svc.Name, + ResourceType: "Service", + Namespace: svc.Namespace, + Path: path, + Port: port, + URL: fmt.Sprintf("http://%s.%s.svc.cluster.local:%s%s", svc.Name, svc.Namespace, port, path), + LastUpdated: time.Now().Format(time.RFC3339), + Annotations: svc.Annotations, + AllowedMethods: allowedMethods, } // Enable health check to validate accessibility @@ -155,27 +162,24 @@ func (r *OpenAPIAggregatorReconciler) processService(ctx context.Context, svc co func (r *OpenAPIAggregatorReconciler) checkAPIHealth(ctx context.Context, apiInfo *observabilityv1alpha1.APIInfo) { logger := log.FromContext(ctx).V(1) - logger.Info("Checking API health", "name", apiInfo.Name, "url", apiInfo.URL) - client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Get(apiInfo.URL) if err != nil { - logger.Info("API health check failed", "name", apiInfo.Name, "error", err) + logger.V(1).Info("API health check failed", "name", apiInfo.Name, "error", err) apiInfo.Error = fmt.Sprintf("Failed to access OpenAPI endpoint: %v", err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - logger.Info("API health check failed", + logger.V(1).Info("API health check failed", "name", apiInfo.Name, - "statusCode", resp.StatusCode, - "headers", resp.Header) + "statusCode", resp.StatusCode) apiInfo.Error = fmt.Sprintf("OpenAPI endpoint returned non-200 status: %d", resp.StatusCode) return } - logger.Info("API health check successful", "name", apiInfo.Name) + logger.V(2).Info("API health check successful", "name", apiInfo.Name) apiInfo.Error = "" } diff --git a/pkg/swagger/server.go b/pkg/swagger/server.go index 51c5c76..156e893 100644 --- a/pkg/swagger/server.go +++ b/pkg/swagger/server.go @@ -19,15 +19,16 @@ var swaggerUI embed.FS // APIMetadata represents metadata about an OpenAPI specification type APIMetadata struct { - Name string `json:"name"` // API name - URL string `json:"url"` // URL to fetch the OpenAPI spec - Title string `json:"title"` // Display title - Version string `json:"version"` // API version - Description string `json:"description"` // API description - ResourceType string `json:"resourceType"` // Type of resource (e.g., Service, Deployment) - ResourceName string `json:"resourceName"` // Name of the Kubernetes resource - Namespace string `json:"namespace"` // Kubernetes namespace - LastUpdated string `json:"lastUpdated"` // Last update timestamp + Name string `json:"name"` // API name + URL string `json:"url"` // URL to fetch the OpenAPI spec + Title string `json:"title"` // Display title + Version string `json:"version"` // API version + Description string `json:"description"` // API description + ResourceType string `json:"resourceType"` // Type of resource (e.g., Service, Deployment) + ResourceName string `json:"resourceName"` // Name of the Kubernetes resource + Namespace string `json:"namespace"` // Kubernetes namespace + LastUpdated string `json:"lastUpdated"` // Last update timestamp + AllowedMethods []string `json:"allowedMethods"` // Allowed HTTP methods for Swagger UI } // Server serves the Swagger UI and aggregated OpenAPI specs @@ -58,14 +59,15 @@ func (s *Server) UpdateSpecs(apis []observabilityv1alpha1.APIInfo) { } metadata := APIMetadata{ - Name: api.Name, - URL: api.URL, - Title: api.Name, - Description: fmt.Sprintf("API from %s/%s", api.Namespace, api.ResourceName), - ResourceType: api.ResourceType, - ResourceName: api.ResourceName, - Namespace: api.Namespace, - LastUpdated: api.LastUpdated, + Name: api.Name, + URL: api.URL, + Title: api.Name, + Description: fmt.Sprintf("API from %s/%s", api.Namespace, api.ResourceName), + ResourceType: api.ResourceType, + ResourceName: api.ResourceName, + Namespace: api.Namespace, + LastUpdated: api.LastUpdated, + AllowedMethods: api.AllowedMethods, } newSpecs[api.Name] = metadata diff --git a/pkg/swagger/swagger-ui/index.html b/pkg/swagger/swagger-ui/index.html index 54cf202..43d912a 100644 --- a/pkg/swagger/swagger-ui/index.html +++ b/pkg/swagger/swagger-ui/index.html @@ -250,10 +250,14 @@

Failed to load API specifications

Failed to load API specifications Date: Sun, 1 Jun 2025 01:48:35 +0900 Subject: [PATCH 07/27] fix error --- .github/workflows/build.yml | 48 +++++++++++------ .../openapiaggregator_controller.go | 53 ++++++++++--------- pkg/swagger/server.go | 7 ++- 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e623b4d..47587ff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,20 +4,20 @@ on: pull_request: branches: [ "main" ] +env: + GO_VERSION: '1.22' + jobs: - build: - strategy: - matrix: - arch: [amd64, arm64] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} + test: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version: ${{ env.GO_VERSION }} cache: true - name: Install dependencies @@ -25,15 +25,29 @@ jobs: go mod download go mod verify - - name: Build - run: | - GOARCH=${{ matrix.arch }} make build - - name: Run tests run: make test - - name: Upload artifacts - uses: actions/upload-artifact@v3 + build: + needs: test + runs-on: ubuntu-latest + strategy: + matrix: + arch: [amd64, arm64] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 with: - name: manager-${{ matrix.arch }} - path: bin/manager-${{ matrix.arch }} + go-version: ${{ env.GO_VERSION }} + cache: true + + - name: Install dependencies + run: go mod download + + - name: Build for ${{ matrix.arch }} + run: | + GOARCH=${{ matrix.arch }} make build + mv bin/manager bin/manager-${{ matrix.arch }} diff --git a/internal/controller/openapiaggregator_controller.go b/internal/controller/openapiaggregator_controller.go index 25afd7f..d8c7323 100644 --- a/internal/controller/openapiaggregator_controller.go +++ b/internal/controller/openapiaggregator_controller.go @@ -19,7 +19,6 @@ package controller import ( "context" "fmt" - "net/http" "strings" "time" @@ -158,30 +157,34 @@ func (r *OpenAPIAggregatorReconciler) processService(ctx context.Context, svc co return apiInfo } -// checkAPIHealth verifies if the OpenAPI endpoint is accessible -func (r *OpenAPIAggregatorReconciler) checkAPIHealth(ctx context.Context, apiInfo *observabilityv1alpha1.APIInfo) { - logger := log.FromContext(ctx).V(1) - - client := &http.Client{Timeout: 5 * time.Second} - resp, err := client.Get(apiInfo.URL) - if err != nil { - logger.V(1).Info("API health check failed", "name", apiInfo.Name, "error", err) - apiInfo.Error = fmt.Sprintf("Failed to access OpenAPI endpoint: %v", err) - return - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - logger.V(1).Info("API health check failed", - "name", apiInfo.Name, - "statusCode", resp.StatusCode) - apiInfo.Error = fmt.Sprintf("OpenAPI endpoint returned non-200 status: %d", resp.StatusCode) - return - } - - logger.V(2).Info("API health check successful", "name", apiInfo.Name) - apiInfo.Error = "" -} +// // checkAPIHealth verifies if the OpenAPI endpoint is accessible +// func (r *OpenAPIAggregatorReconciler) checkAPIHealth(ctx context.Context, apiInfo *observabilityv1alpha1.APIInfo) { +// logger := log.FromContext(ctx).V(1) + +// client := &http.Client{Timeout: 5 * time.Second} +// resp, err := client.Get(apiInfo.URL) +// if err != nil { +// logger.V(1).Info("API health check failed", "name", apiInfo.Name, "error", err) +// apiInfo.Error = fmt.Sprintf("Failed to access OpenAPI endpoint: %v", err) +// return +// } +// defer func() { +// if err := resp.Body.Close(); err != nil { +// logger.Error(err, "Failed to close response body") +// } +// }() + +// if resp.StatusCode != http.StatusOK { +// logger.V(1).Info("API health check failed", +// "name", apiInfo.Name, +// "statusCode", resp.StatusCode) +// apiInfo.Error = fmt.Sprintf("OpenAPI endpoint returned non-200 status: %d", resp.StatusCode) +// return +// } + +// logger.V(2).Info("API health check successful", "name", apiInfo.Name) +// apiInfo.Error = "" +// } // SetupWithManager sets up the controller with the Manager. func (r *OpenAPIAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) error { diff --git a/pkg/swagger/server.go b/pkg/swagger/server.go index 156e893..893018b 100644 --- a/pkg/swagger/server.go +++ b/pkg/swagger/server.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "log" "net/http" "os" "strings" @@ -139,7 +140,11 @@ func (s *Server) serveIndividualSpec(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("Failed to fetch spec: %v", err), http.StatusInternalServerError) return } - defer resp.Body.Close() + defer func() { + if err := resp.Body.Close(); err != nil { + log.Printf("Failed to close response body: %v", err) + } + }() if resp.StatusCode != http.StatusOK { http.Error(w, fmt.Sprintf("Failed to fetch spec, status: %d", resp.StatusCode), resp.StatusCode) From 9d7f73d3c76966862597ca7cee1536c45dd51252 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 00:39:21 +0900 Subject: [PATCH 08/27] lint, build fix --- Makefile | 23 ++++++++-- go.mod | 2 - .../openapiaggregator_controller.go | 43 +++++++++++++++---- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 6167562..d3f459a 100644 --- a/Makefile +++ b/Makefile @@ -155,9 +155,20 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes .PHONY: build build: manifests generate fmt vet ## Build manager binary. - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=amd64 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_amd64 cmd/main.go - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=arm64 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_arm64 cmd/main.go - ln -sf manager_$(ARCH) bin/manager + @mkdir -p bin + @if [ "$(GOARCH)" = "amd64" ] || [ "$(GOARCH)" = "" ]; then \ + echo "Building amd64 binary..." ;\ + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=amd64 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_amd64 cmd/main.go ;\ + fi + @if [ "$(GOARCH)" = "arm64" ] || [ "$(GOARCH)" = "" ]; then \ + echo "Building arm64 binary..." ;\ + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=arm64 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_arm64 cmd/main.go ;\ + fi + @if [ "$(GOARCH)" = "" ]; then \ + ln -sf manager_$$(go env GOARCH) bin/manager ;\ + else \ + ln -sf manager_$(GOARCH) bin/manager ;\ + fi .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. @@ -287,8 +298,12 @@ set -e; \ package=$(2)@$(3) ;\ echo "Downloading and building $${package} for $$(go env GOARCH)" ;\ rm -f $(1) || true ;\ -GOARCH=$$(go env GOARCH) GOBIN=$(LOCALBIN) go install $${package} ;\ +TEMP_DIR=$$(mktemp -d) ;\ +cd $$TEMP_DIR ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +cd - ;\ mv $(1) $(1)-$(3)-$$(go env GOARCH) ;\ +rm -rf $$TEMP_DIR ;\ } ;\ ln -sf $(1)-$(3)-$$(go env GOARCH) $(1) endef diff --git a/go.mod b/go.mod index ab7fa34..02941a3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ toolchain go1.23.9 require ( github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 - github.com/stretchr/testify v1.9.0 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 @@ -53,7 +52,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect diff --git a/internal/controller/openapiaggregator_controller.go b/internal/controller/openapiaggregator_controller.go index d8c7323..5aa395d 100644 --- a/internal/controller/openapiaggregator_controller.go +++ b/internal/controller/openapiaggregator_controller.go @@ -25,6 +25,8 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -79,12 +81,22 @@ func (r *OpenAPIAggregatorReconciler) Reconcile(ctx context.Context, req ctrl.Re } } - // Simply update the status with the new list - // Any removed services will naturally be excluded - instance.Status.CollectedAPIs = collectedAPIs - if err := r.Status().Update(ctx, instance); err != nil { - logger.Error(err, "Failed to update OpenAPIAggregator status") - return ctrl.Result{}, err + // Update status with retry on conflict + retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + // Get the latest version + latest := &observabilityv1alpha1.OpenAPIAggregator{} + if err := r.Get(ctx, req.NamespacedName, latest); err != nil { + return err + } + + // Update status + latest.Status.CollectedAPIs = collectedAPIs + return r.Status().Update(ctx, latest) + }) + + if retryErr != nil { + logger.Error(retryErr, "Failed to update OpenAPIAggregator status") + return ctrl.Result{}, retryErr } // Update Swagger UI with collected specs @@ -102,7 +114,7 @@ func (r *OpenAPIAggregatorReconciler) processService(ctx context.Context, svc co // Check if the service has the required swagger annotation if svc.Annotations[instance.Spec.SwaggerAnnotation] != "true" { - logger.Info("Skipping service - missing swagger annotation", + logger.V(2).Info("Skipping service - missing swagger annotation", "service", svc.Name, "namespace", svc.Namespace, "requiredAnnotation", instance.Spec.SwaggerAnnotation) @@ -201,6 +213,21 @@ func (r *OpenAPIAggregatorReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&observabilityv1alpha1.OpenAPIAggregator{}). - Watches(&corev1.Service{}, &handler.EnqueueRequestForObject{}). + Watches( + &corev1.Service{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []ctrl.Request { + svc := obj.(*corev1.Service) + // 서비스에 swagger 관련 어노테이션이 있는 경우에만 리컨실레이션 트리거 + if val, ok := svc.Annotations["openapi.aggregator.io/swagger"]; ok && val == "true" { + return []ctrl.Request{ + {NamespacedName: types.NamespacedName{ + Name: "openapi-aggregator", + Namespace: svc.Namespace, + }}, + } + } + return nil + }), + ). Complete(r) } From 8a1b20fbeb30b6985600902c1619cbbf6147354c Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 00:56:56 +0900 Subject: [PATCH 09/27] fix build --- Makefile | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index d3f459a..10b7442 100644 --- a/Makefile +++ b/Makefile @@ -71,8 +71,6 @@ GOBIN=$(shell go env GOBIN) endif LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) # CONTAINER_TOOL defines the container tool to be used for building images. # Be aware that the target commands are only tested with Docker which is @@ -252,7 +250,9 @@ undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/. ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): + +## Create bin directory if it doesn't exist +bin_dir: ## Create bin directory mkdir -p $(LOCALBIN) ## Tool Binaries @@ -269,24 +269,20 @@ ENVTEST_VERSION ?= release-0.19 GOLANGCI_LINT_VERSION ?= v1.59.1 .PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. -$(KUSTOMIZE): $(LOCALBIN) - $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) +kustomize: bin_dir ## Download kustomize locally if necessary. + [ -f $(KUSTOMIZE) ] || $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) .PHONY: controller-gen -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. -$(CONTROLLER_GEN): $(LOCALBIN) - $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) +controller-gen: bin_dir ## Download controller-gen locally if necessary. + [ -f $(CONTROLLER_GEN) ] || $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) .PHONY: envtest -envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. -$(ENVTEST): $(LOCALBIN) - $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) +envtest: bin_dir ## Download setup-envtest locally if necessary. + [ -f $(ENVTEST) ] || $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) .PHONY: golangci-lint -golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. -$(GOLANGCI_LINT): $(LOCALBIN) - $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) +golangci-lint: bin_dir ## Download golangci-lint locally if necessary. + [ -f $(GOLANGCI_LINT) ] || $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary @@ -300,7 +296,7 @@ echo "Downloading and building $${package} for $$(go env GOARCH)" ;\ rm -f $(1) || true ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ -GOBIN=$(LOCALBIN) go install $${package} ;\ +GOOS=$$(go env GOOS) GOARCH=$$(go env GOARCH) CGO_ENABLED=0 go build -o $(1) $${package} ;\ cd - ;\ mv $(1) $(1)-$(3)-$$(go env GOARCH) ;\ rm -rf $$TEMP_DIR ;\ From 24192e4e8af78e2e0b653dfbee83a7b5f759d4c7 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 09:09:06 +0900 Subject: [PATCH 10/27] test work --- Makefile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 10b7442..cefaee5 100644 --- a/Makefile +++ b/Makefile @@ -277,8 +277,10 @@ controller-gen: bin_dir ## Download controller-gen locally if necessary. [ -f $(CONTROLLER_GEN) ] || $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) .PHONY: envtest -envtest: bin_dir ## Download setup-envtest locally if necessary. - [ -f $(ENVTEST) ] || $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) +envtest: ## Download envtest-setup locally if necessary + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,release-0.19) + KUBEBUILDER_ASSETS=$$($(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path) \ + go test $(GO_TEST_FLAGS) $(shell go list ./... | grep -v /e2e) -coverprofile cover.out .PHONY: golangci-lint golangci-lint: bin_dir ## Download golangci-lint locally if necessary. @@ -289,19 +291,17 @@ golangci-lint: bin_dir ## Download golangci-lint locally if necessary. # $2 - package url which can be installed # $3 - specific version of package define go-install-tool -@[ -f "$(1)-$(3)-$$(go env GOARCH)" ] || { \ +[ -f "$(1)-$(3)-$$(go env GOARCH)" ] || { \ set -e; \ -package=$(2)@$(3) ;\ -echo "Downloading and building $${package} for $$(go env GOARCH)" ;\ -rm -f $(1) || true ;\ +echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ -GOOS=$$(go env GOOS) GOARCH=$$(go env GOARCH) CGO_ENABLED=0 go build -o $(1) $${package} ;\ -cd - ;\ -mv $(1) $(1)-$(3)-$$(go env GOARCH) ;\ +GO111MODULE=on go mod init tmp ;\ +GO111MODULE=on GOBIN=$$(dirname $(1)) go install $(2)@$(3) ;\ +mv "$$(dirname $(1))/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ rm -rf $$TEMP_DIR ;\ } ;\ -ln -sf $(1)-$(3)-$$(go env GOARCH) $(1) +ln -sf "$(1)-$(3)-$$(go env GOARCH)" "$(1)" endef .PHONY: operator-sdk From 9d264460ab9fd388fffd3eb1dc2d4b3c58e5dc5e Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 10:15:16 +0900 Subject: [PATCH 11/27] build fix --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cefaee5..23fa893 100644 --- a/Makefile +++ b/Makefile @@ -292,12 +292,14 @@ golangci-lint: bin_dir ## Download golangci-lint locally if necessary. # $3 - specific version of package define go-install-tool [ -f "$(1)-$(3)-$$(go env GOARCH)" ] || { \ -set -e; \ +set -e ;\ echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ +ORIG_DIR=$$(pwd) ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ -GO111MODULE=on GOBIN=$$(dirname $(1)) go install $(2)@$(3) ;\ +GO111MODULE=on GOBIN=$$ORIG_DIR/$$(dirname $(1)) go install $(2)@$(3) ;\ +cd $$ORIG_DIR ;\ mv "$$(dirname $(1))/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ rm -rf $$TEMP_DIR ;\ } ;\ From 19830204a9da6435b66bcfbd814d287dfcb17b78 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 10:30:31 +0900 Subject: [PATCH 12/27] build fix2 --- .github/workflows/build.yml | 11 ++++++++++- Makefile | 11 ++++++++--- bin/controller-gen | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47587ff..94376e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,11 @@ jobs: go mod download go mod verify + - name: Setup bin directory + run: | + mkdir -p bin + chmod 755 bin + - name: Run tests run: make test @@ -47,7 +52,11 @@ jobs: - name: Install dependencies run: go mod download + - name: Setup bin directory + run: | + mkdir -p bin + chmod 755 bin + - name: Build for ${{ matrix.arch }} run: | GOARCH=${{ matrix.arch }} make build - mv bin/manager bin/manager-${{ matrix.arch }} diff --git a/Makefile b/Makefile index 23fa893..a48469c 100644 --- a/Makefile +++ b/Makefile @@ -291,8 +291,9 @@ golangci-lint: bin_dir ## Download golangci-lint locally if necessary. # $2 - package url which can be installed # $3 - specific version of package define go-install-tool -[ -f "$(1)-$(3)-$$(go env GOARCH)" ] || { \ +[ -f "$(1)" ] || { \ set -e ;\ +mkdir -p $$(dirname $(1)) ;\ echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ ORIG_DIR=$$(pwd) ;\ TEMP_DIR=$$(mktemp -d) ;\ @@ -300,10 +301,14 @@ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ GO111MODULE=on GOBIN=$$ORIG_DIR/$$(dirname $(1)) go install $(2)@$(3) ;\ cd $$ORIG_DIR ;\ -mv "$$(dirname $(1))/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ +if [ -f "$$(dirname $(1))/$$(basename $(1))" ]; then \ + mv "$$(dirname $(1))/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ +fi ;\ rm -rf $$TEMP_DIR ;\ } ;\ -ln -sf "$(1)-$(3)-$$(go env GOARCH)" "$(1)" +if [ -f "$(1)-$(3)-$$(go env GOARCH)" ]; then \ + ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" ;\ +fi endef .PHONY: operator-sdk diff --git a/bin/controller-gen b/bin/controller-gen index 67c736b..c02c099 120000 --- a/bin/controller-gen +++ b/bin/controller-gen @@ -1 +1 @@ -/home/inhwanhwang/golang/openapi-aggregator-operator/bin/controller-gen-v0.16.1-arm64 \ No newline at end of file +controller-gen-v0.16.1-arm64 \ No newline at end of file From 4e2bcc8b3ed0b7934a47e174c200f3b6b4aa658d Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 10:41:46 +0900 Subject: [PATCH 13/27] build fix3 --- .github/workflows/build.yml | 18 +++++++++--------- Makefile | 14 ++++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94376e2..9142d42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: env: GO_VERSION: '1.22' + WORKSPACE_DIR: ${{ github.workspace }} jobs: test: @@ -25,10 +26,11 @@ jobs: go mod download go mod verify - - name: Setup bin directory + - name: Setup build environment run: | - mkdir -p bin - chmod 755 bin + mkdir -p ${{ env.WORKSPACE_DIR }}/bin + chmod 755 ${{ env.WORKSPACE_DIR }}/bin + echo "WORKSPACE_DIR=${{ env.WORKSPACE_DIR }}" >> $GITHUB_ENV - name: Run tests run: make test @@ -49,13 +51,11 @@ jobs: go-version: ${{ env.GO_VERSION }} cache: true - - name: Install dependencies - run: go mod download - - - name: Setup bin directory + - name: Setup build environment run: | - mkdir -p bin - chmod 755 bin + mkdir -p ${{ env.WORKSPACE_DIR }}/bin + chmod 755 ${{ env.WORKSPACE_DIR }}/bin + echo "WORKSPACE_DIR=${{ env.WORKSPACE_DIR }}" >> $GITHUB_ENV - name: Build for ${{ matrix.arch }} run: | diff --git a/Makefile b/Makefile index a48469c..e90db17 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ ARCH ?= $(shell go env GOARCH) GOOS ?= linux PLATFORMS ?= linux/arm64,linux/amd64 +# Get the workspace directory +WORKSPACE_DIR ?= $(shell pwd) + # Version information GIT_VERSION ?= $(shell git describe --tags --always) FILE_VERSION ?= $(shell awk -F= '/^operator=/ {print $$2}' versions.txt) @@ -293,17 +296,16 @@ golangci-lint: bin_dir ## Download golangci-lint locally if necessary. define go-install-tool [ -f "$(1)" ] || { \ set -e ;\ -mkdir -p $$(dirname $(1)) ;\ +mkdir -p $(WORKSPACE_DIR)/bin ;\ echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ -ORIG_DIR=$$(pwd) ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ -GO111MODULE=on GOBIN=$$ORIG_DIR/$$(dirname $(1)) go install $(2)@$(3) ;\ -cd $$ORIG_DIR ;\ -if [ -f "$$(dirname $(1))/$$(basename $(1))" ]; then \ - mv "$$(dirname $(1))/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ +GO111MODULE=on GOBIN=$(WORKSPACE_DIR)/bin go install $(2)@$(3) ;\ +if [ -f "$(WORKSPACE_DIR)/bin/$$(basename $(1))" ]; then \ + mv "$(WORKSPACE_DIR)/bin/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ fi ;\ +cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ } ;\ if [ -f "$(1)-$(3)-$$(go env GOARCH)" ]; then \ From a0a28dc3f970a861fb9874b68f28742851198683 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 10:48:18 +0900 Subject: [PATCH 14/27] build fix4 --- .github/workflows/build.yml | 8 ++++---- Makefile | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9142d42..ae638b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,6 @@ jobs: run: | mkdir -p ${{ env.WORKSPACE_DIR }}/bin chmod 755 ${{ env.WORKSPACE_DIR }}/bin - echo "WORKSPACE_DIR=${{ env.WORKSPACE_DIR }}" >> $GITHUB_ENV - name: Run tests run: make test @@ -55,8 +54,9 @@ jobs: run: | mkdir -p ${{ env.WORKSPACE_DIR }}/bin chmod 755 ${{ env.WORKSPACE_DIR }}/bin - echo "WORKSPACE_DIR=${{ env.WORKSPACE_DIR }}" >> $GITHUB_ENV + echo "GOOS=linux" >> $GITHUB_ENV + echo "CGO_ENABLED=0" >> $GITHUB_ENV + echo "GOARCH=${{ matrix.arch }}" >> $GITHUB_ENV - name: Build for ${{ matrix.arch }} - run: | - GOARCH=${{ matrix.arch }} make build + run: make build diff --git a/Makefile b/Makefile index e90db17..02f80b1 100644 --- a/Makefile +++ b/Makefile @@ -301,16 +301,16 @@ echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ -GO111MODULE=on GOBIN=$(WORKSPACE_DIR)/bin go install $(2)@$(3) ;\ -if [ -f "$(WORKSPACE_DIR)/bin/$$(basename $(1))" ]; then \ - mv "$(WORKSPACE_DIR)/bin/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ -fi ;\ +case "$$(go env GOHOSTOS)" in \ + darwin) \ + CGO_ENABLED=0 GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOHOSTARCH) go build -o $(1)-$(3)-$$(go env GOARCH) $(2)@$(3) ;; \ + linux) \ + CGO_ENABLED=0 GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOARCH) go build -o $(1)-$(3)-$$(go env GOARCH) $(2)@$(3) ;; \ +esac ;\ cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ } ;\ -if [ -f "$(1)-$(3)-$$(go env GOARCH)" ]; then \ - ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" ;\ -fi +ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" endef .PHONY: operator-sdk From 22a5197d8fe1715b68d62bef483e1e6bb3b183d1 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 10:51:58 +0900 Subject: [PATCH 15/27] build fix5 --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 02f80b1..e364212 100644 --- a/Makefile +++ b/Makefile @@ -159,11 +159,11 @@ build: manifests generate fmt vet ## Build manager binary. @mkdir -p bin @if [ "$(GOARCH)" = "amd64" ] || [ "$(GOARCH)" = "" ]; then \ echo "Building amd64 binary..." ;\ - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=amd64 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_amd64 cmd/main.go ;\ + GOOS=$(GOOS) GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_amd64 cmd/main.go ;\ fi @if [ "$(GOARCH)" = "arm64" ] || [ "$(GOARCH)" = "" ]; then \ echo "Building arm64 binary..." ;\ - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=arm64 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_arm64 cmd/main.go ;\ + GOOS=$(GOOS) GOARCH=arm64 CGO_ENABLED=0 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_arm64 cmd/main.go ;\ fi @if [ "$(GOARCH)" = "" ]; then \ ln -sf manager_$$(go env GOARCH) bin/manager ;\ @@ -301,16 +301,16 @@ echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ -case "$$(go env GOHOSTOS)" in \ - darwin) \ - CGO_ENABLED=0 GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOHOSTARCH) go build -o $(1)-$(3)-$$(go env GOARCH) $(2)@$(3) ;; \ - linux) \ - CGO_ENABLED=0 GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOARCH) go build -o $(1)-$(3)-$$(go env GOARCH) $(2)@$(3) ;; \ -esac ;\ +GO111MODULE=on GOBIN=$(WORKSPACE_DIR)/bin go install $(2)@$(3) ;\ +if [ -f "$(WORKSPACE_DIR)/bin/$$(basename $(1))" ]; then \ + mv "$(WORKSPACE_DIR)/bin/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ +fi ;\ cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ } ;\ -ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" +if [ -f "$(1)-$(3)-$$(go env GOARCH)" ]; then \ + ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" ;\ +fi endef .PHONY: operator-sdk From c40d35bb36a962a712fe737cc3f7433c92803b56 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 11:15:15 +0900 Subject: [PATCH 16/27] build fix6 --- .github/workflows/build.yml | 17 +++++++++-------- Makefile | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae638b7..65f0f77 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,6 @@ on: env: GO_VERSION: '1.22' - WORKSPACE_DIR: ${{ github.workspace }} jobs: test: @@ -26,10 +25,10 @@ jobs: go mod download go mod verify - - name: Setup build environment + - name: Setup environment run: | - mkdir -p ${{ env.WORKSPACE_DIR }}/bin - chmod 755 ${{ env.WORKSPACE_DIR }}/bin + mkdir -p bin + chmod 755 bin - name: Run tests run: make test @@ -52,11 +51,13 @@ jobs: - name: Setup build environment run: | - mkdir -p ${{ env.WORKSPACE_DIR }}/bin - chmod 755 ${{ env.WORKSPACE_DIR }}/bin + mkdir -p bin + chmod 755 bin echo "GOOS=linux" >> $GITHUB_ENV echo "CGO_ENABLED=0" >> $GITHUB_ENV echo "GOARCH=${{ matrix.arch }}" >> $GITHUB_ENV - - name: Build for ${{ matrix.arch }} - run: make build + - name: Build binary + run: | + go mod download + GOARCH=${{ matrix.arch }} make build diff --git a/Makefile b/Makefile index e364212..3bdeced 100644 --- a/Makefile +++ b/Makefile @@ -296,21 +296,23 @@ golangci-lint: bin_dir ## Download golangci-lint locally if necessary. define go-install-tool [ -f "$(1)" ] || { \ set -e ;\ -mkdir -p $(WORKSPACE_DIR)/bin ;\ +mkdir -p $(LOCALBIN) ;\ echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ -GO111MODULE=on GOBIN=$(WORKSPACE_DIR)/bin go install $(2)@$(3) ;\ -if [ -f "$(WORKSPACE_DIR)/bin/$$(basename $(1))" ]; then \ - mv "$(WORKSPACE_DIR)/bin/$$(basename $(1))" "$(1)-$(3)-$$(go env GOARCH)" ;\ +if [ "$$(go env GOOS)" = "linux" ] && [ "$$(go env GOARCH)" = "arm64" ]; then \ + CGO_ENABLED=0 GOARCH=arm64 go build -o "$(1)-$(3)-arm64" $(2)@$(3) ;\ +elif [ "$$(go env GOOS)" = "linux" ] && [ "$$(go env GOARCH)" = "amd64" ]; then \ + CGO_ENABLED=0 GOARCH=amd64 go build -o "$(1)-$(3)-amd64" $(2)@$(3) ;\ +else \ + CGO_ENABLED=0 go build -o "$(1)-$(3)-$$(go env GOARCH)" $(2)@$(3) ;\ fi ;\ +mv "$(1)-$(3)-$$(go env GOARCH)" "$(LOCALBIN)/" ;\ cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ -} ;\ -if [ -f "$(1)-$(3)-$$(go env GOARCH)" ]; then \ - ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" ;\ -fi +ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" ;\ +} endef .PHONY: operator-sdk From 6717bb5264c0743e58609e4495d66aeb541cffa3 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 11:24:36 +0900 Subject: [PATCH 17/27] build fix7 --- Makefile | 109 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 45 deletions(-) diff --git a/Makefile b/Makefile index 3bdeced..14ccf13 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,17 @@ -# Common variables and settings -ARCH ?= $(shell go env GOARCH) +# ==================== +# Tool Installation +# ==================== + +# Tools will be installed to this directory +LOCALBIN ?= $(shell pwd)/bin +TOOLS_DIR := $(LOCALBIN) +WORKSPACE_DIR ?= $(CURDIR) + +# Cross compilation settings GOOS ?= linux -PLATFORMS ?= linux/arm64,linux/amd64 +GOARCH ?= $(shell go env GOARCH) +CGO_ENABLED ?= 0 +COMMON_LDFLAGS ?= -s -w # Get the workspace directory WORKSPACE_DIR ?= $(shell pwd) @@ -135,15 +145,20 @@ fmt: ## Run go fmt against code. vet: ## Run go vet against code. go vet ./... -.PHONY: test +.PHONY: test test-e2e test-coverage test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \ + go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -v -race -# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors. -.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. -test-e2e: +# Run e2e tests using Kind +test-e2e: docker-build ## Run e2e tests against a Kind cluster go test ./test/e2e/ -v -ginkgo.v +# Run tests with coverage report +test-coverage: test ## Run tests and generate coverage report + go tool cover -html=cover.out -o coverage.html + @echo "Coverage report generated at coverage.html" + .PHONY: lint lint: golangci-lint ## Run golangci-lint linter $(GOLANGCI_LINT) run @@ -154,22 +169,26 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes ##@ Build -.PHONY: build -build: manifests generate fmt vet ## Build manager binary. +.PHONY: build build-all +build: manifests generate fmt vet ## Build manager binary for current architecture. + @mkdir -p bin + @echo "Building $(GOARCH) binary..." + GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) go build \ + -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ + -o bin/manager_$(GOARCH) cmd/main.go + @ln -sf manager_$(GOARCH) bin/manager + +build-all: manifests generate fmt vet ## Build manager binaries for all supported architectures. @mkdir -p bin - @if [ "$(GOARCH)" = "amd64" ] || [ "$(GOARCH)" = "" ]; then \ - echo "Building amd64 binary..." ;\ - GOOS=$(GOOS) GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_amd64 cmd/main.go ;\ - fi - @if [ "$(GOARCH)" = "arm64" ] || [ "$(GOARCH)" = "" ]; then \ - echo "Building arm64 binary..." ;\ - GOOS=$(GOOS) GOARCH=arm64 CGO_ENABLED=0 go build -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" -o bin/manager_arm64 cmd/main.go ;\ - fi - @if [ "$(GOARCH)" = "" ]; then \ - ln -sf manager_$$(go env GOARCH) bin/manager ;\ - else \ - ln -sf manager_$(GOARCH) bin/manager ;\ - fi + @echo "Building amd64 binary..." + GOOS=$(GOOS) GOARCH=amd64 CGO_ENABLED=$(CGO_ENABLED) go build \ + -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ + -o bin/manager_amd64 cmd/main.go + @echo "Building arm64 binary..." + GOOS=$(GOOS) GOARCH=arm64 CGO_ENABLED=$(CGO_ENABLED) go build \ + -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ + -o bin/manager_arm64 cmd/main.go + @ln -sf manager_$$(go env GOARCH) bin/manager .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. @@ -295,23 +314,20 @@ golangci-lint: bin_dir ## Download golangci-lint locally if necessary. # $3 - specific version of package define go-install-tool [ -f "$(1)" ] || { \ -set -e ;\ -mkdir -p $(LOCALBIN) ;\ -echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ -TEMP_DIR=$$(mktemp -d) ;\ -cd $$TEMP_DIR ;\ -GO111MODULE=on go mod init tmp ;\ -if [ "$$(go env GOOS)" = "linux" ] && [ "$$(go env GOARCH)" = "arm64" ]; then \ - CGO_ENABLED=0 GOARCH=arm64 go build -o "$(1)-$(3)-arm64" $(2)@$(3) ;\ -elif [ "$$(go env GOOS)" = "linux" ] && [ "$$(go env GOARCH)" = "amd64" ]; then \ - CGO_ENABLED=0 GOARCH=amd64 go build -o "$(1)-$(3)-amd64" $(2)@$(3) ;\ -else \ - CGO_ENABLED=0 go build -o "$(1)-$(3)-$$(go env GOARCH)" $(2)@$(3) ;\ -fi ;\ -mv "$(1)-$(3)-$$(go env GOARCH)" "$(LOCALBIN)/" ;\ -cd $(WORKSPACE_DIR) ;\ -rm -rf $$TEMP_DIR ;\ -ln -sf "$$(basename $(1))-$(3)-$$(go env GOARCH)" "$(1)" ;\ + set -e ;\ + mkdir -p $(LOCALBIN) ;\ + echo "Downloading and building $(2)@$(3) for $(GOARCH)" ;\ + TEMP_DIR=$$(mktemp -d) ;\ + cd $$TEMP_DIR ;\ + GO111MODULE=on go mod init tmp ;\ + GO111MODULE=on go mod edit -go=1.21 ;\ + GO111MODULE=on go get $(2)@$(3) ;\ + CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ + go build -ldflags "${COMMON_LDFLAGS}" -o "$(1)-$(3)-$(GOARCH)" $$(go list -f '{{.ImportPath}}' ./... | grep -e "^$(2)$$") ;\ + mv "$(1)-$(3)-$(GOARCH)" "$(LOCALBIN)/" ;\ + cd $(WORKSPACE_DIR) ;\ + rm -rf $$TEMP_DIR ;\ + ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ } endef @@ -391,8 +407,11 @@ catalog-push: ## Push a catalog image. ##@ Development tools .PHONY: install-tools -install-tools: ## Install development tools - $(LOCALBIN)/controller-gen --version || $(MAKE) controller-gen - $(LOCALBIN)/kustomize --version || $(MAKE) kustomize - $(LOCALBIN)/envtest --version || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest - setup-envtest use --bin-dir $(LOCALBIN) 1.24.2 +install-tools: bin_dir ## Install all development tools + $(MAKE) controller-gen + $(MAKE) kustomize + $(MAKE) envtest + $(MAKE) golangci-lint + $(MAKE) operator-sdk + @echo "Installing envtest assets..." + $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) From 5d1bd105e268aef39048baa429b2c06daf515644 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 11:26:18 +0900 Subject: [PATCH 18/27] build fix8 --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 14ccf13..862a11e 100644 --- a/Makefile +++ b/Makefile @@ -322,8 +322,9 @@ define go-install-tool GO111MODULE=on go mod init tmp ;\ GO111MODULE=on go mod edit -go=1.21 ;\ GO111MODULE=on go get $(2)@$(3) ;\ + PKG_PATH=$$(GO111MODULE=on go list -f '{{.ImportPath}}' -m $(2)) ;\ CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go build -ldflags "${COMMON_LDFLAGS}" -o "$(1)-$(3)-$(GOARCH)" $$(go list -f '{{.ImportPath}}' ./... | grep -e "^$(2)$$") ;\ + go build -ldflags "${COMMON_LDFLAGS}" -o "$(1)-$(3)-$(GOARCH)" $$PKG_PATH ;\ mv "$(1)-$(3)-$(GOARCH)" "$(LOCALBIN)/" ;\ cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ From 2248d713c871668bfc4533496107189acd125aca Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 11:31:05 +0900 Subject: [PATCH 19/27] build fix8 --- .github/workflows/build.yml | 42 ++++++++++--------------------------- Makefile | 28 ++++++++++++------------- 2 files changed, 25 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65f0f77..dced612 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,33 +8,7 @@ env: GO_VERSION: '1.22' jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache: true - - - name: Install dependencies - run: | - go mod download - go mod verify - - - name: Setup environment - run: | - mkdir -p bin - chmod 755 bin - - - name: Run tests - run: make test - - build: - needs: test + build-and-test: runs-on: ubuntu-latest strategy: matrix: @@ -49,7 +23,7 @@ jobs: go-version: ${{ env.GO_VERSION }} cache: true - - name: Setup build environment + - name: Setup environment run: | mkdir -p bin chmod 755 bin @@ -57,7 +31,13 @@ jobs: echo "CGO_ENABLED=0" >> $GITHUB_ENV echo "GOARCH=${{ matrix.arch }}" >> $GITHUB_ENV - - name: Build binary + - name: Install dependencies + run: go mod download + + - name: Build and Test run: | - go mod download - GOARCH=${{ matrix.arch }} make build + # Run tests first (includes necessary codegen) + make test + + # Build for current architecture + GOARCH=${{ matrix.arch }} make build-only diff --git a/Makefile b/Makefile index 862a11e..e575b64 100644 --- a/Makefile +++ b/Makefile @@ -169,8 +169,11 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes ##@ Build -.PHONY: build build-all +.PHONY: build build-only build-all build: manifests generate fmt vet ## Build manager binary for current architecture. + @$(MAKE) build-only + +build-only: ## Build manager binary without preprocessing steps @mkdir -p bin @echo "Building $(GOARCH) binary..." GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) go build \ @@ -316,19 +319,16 @@ define go-install-tool [ -f "$(1)" ] || { \ set -e ;\ mkdir -p $(LOCALBIN) ;\ - echo "Downloading and building $(2)@$(3) for $(GOARCH)" ;\ - TEMP_DIR=$$(mktemp -d) ;\ - cd $$TEMP_DIR ;\ - GO111MODULE=on go mod init tmp ;\ - GO111MODULE=on go mod edit -go=1.21 ;\ - GO111MODULE=on go get $(2)@$(3) ;\ - PKG_PATH=$$(GO111MODULE=on go list -f '{{.ImportPath}}' -m $(2)) ;\ - CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go build -ldflags "${COMMON_LDFLAGS}" -o "$(1)-$(3)-$(GOARCH)" $$PKG_PATH ;\ - mv "$(1)-$(3)-$(GOARCH)" "$(LOCALBIN)/" ;\ - cd $(WORKSPACE_DIR) ;\ - rm -rf $$TEMP_DIR ;\ - ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ + echo "Downloading and installing $(2)@$(3)" ;\ + GOBIN=$(LOCALBIN) CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ + go install $(2)@$(3) ;\ + if [ -f "$(LOCALBIN)/$$(basename $(2))" ]; then \ + mv "$(LOCALBIN)/$$(basename $(2))" "$(1)-$(3)-$(GOARCH)" ;\ + ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ + else \ + echo "Binary not found after installation" ;\ + exit 1 ;\ + fi \ } endef From 69e1a9ce638a06ed1ff419af36501cf46cb815b5 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 11:33:20 +0900 Subject: [PATCH 20/27] build fix10 --- Makefile | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index e575b64..690e3b5 100644 --- a/Makefile +++ b/Makefile @@ -311,7 +311,7 @@ envtest: ## Download envtest-setup locally if necessary golangci-lint: bin_dir ## Download golangci-lint locally if necessary. [ -f $(GOLANGCI_LINT) ] || $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) -# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# go-install-tool will build any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary # $2 - package url which can be installed # $3 - specific version of package @@ -319,16 +319,17 @@ define go-install-tool [ -f "$(1)" ] || { \ set -e ;\ mkdir -p $(LOCALBIN) ;\ - echo "Downloading and installing $(2)@$(3)" ;\ - GOBIN=$(LOCALBIN) CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go install $(2)@$(3) ;\ - if [ -f "$(LOCALBIN)/$$(basename $(2))" ]; then \ - mv "$(LOCALBIN)/$$(basename $(2))" "$(1)-$(3)-$(GOARCH)" ;\ - ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ - else \ - echo "Binary not found after installation" ;\ - exit 1 ;\ - fi \ + echo "Downloading and building $(2)@$(3)" ;\ + TEMP_DIR=$$(mktemp -d) ;\ + cd $$TEMP_DIR ;\ + GO111MODULE=on go mod init tmp ;\ + GO111MODULE=on go get $(2)@$(3) ;\ + CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ + go build -o "$(1)-$(3)-$(GOARCH)" $(2) ;\ + mv "$(1)-$(3)-$(GOARCH)" "$(LOCALBIN)/" ;\ + cd $(WORKSPACE_DIR) ;\ + rm -rf $$TEMP_DIR ;\ + ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ } endef From 92c1264a3d810693c02d8142ec5371cc1ea723d5 Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 13:04:45 +0900 Subject: [PATCH 21/27] build fix11 --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 690e3b5..c7504dd 100644 --- a/Makefile +++ b/Makefile @@ -326,9 +326,15 @@ define go-install-tool GO111MODULE=on go get $(2)@$(3) ;\ CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ go build -o "$(1)-$(3)-$(GOARCH)" $(2) ;\ + if [ -f "$(LOCALBIN)/$$(basename $(1))-$(3)-$(GOARCH)" ]; then \ + rm "$(LOCALBIN)/$$(basename $(1))-$(3)-$(GOARCH)" ;\ + fi ;\ mv "$(1)-$(3)-$(GOARCH)" "$(LOCALBIN)/" ;\ cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ + if [ -L "$(1)" ]; then \ + rm "$(1)" ;\ + fi ;\ ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ } endef From ef951cb2f6cb13b163d72ccb13bed00db4fef9cc Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 13:09:50 +0900 Subject: [PATCH 22/27] build fix12 --- Makefile | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index c7504dd..28c5ca3 100644 --- a/Makefile +++ b/Makefile @@ -324,18 +324,21 @@ define go-install-tool cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ GO111MODULE=on go get $(2)@$(3) ;\ + BASE_NAME=$$(basename $(1)) ;\ + ARCH_SUFFIX="-$(3)-$(GOARCH)" ;\ + BINARY_NAME="$$BASE_NAME$$ARCH_SUFFIX" ;\ CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ - go build -o "$(1)-$(3)-$(GOARCH)" $(2) ;\ - if [ -f "$(LOCALBIN)/$$(basename $(1))-$(3)-$(GOARCH)" ]; then \ - rm "$(LOCALBIN)/$$(basename $(1))-$(3)-$(GOARCH)" ;\ + go build -o "$$BINARY_NAME" $(2) ;\ + if [ -f "$(LOCALBIN)/$$BINARY_NAME" ]; then \ + rm "$(LOCALBIN)/$$BINARY_NAME" ;\ fi ;\ - mv "$(1)-$(3)-$(GOARCH)" "$(LOCALBIN)/" ;\ - cd $(WORKSPACE_DIR) ;\ - rm -rf $$TEMP_DIR ;\ + mv "$$BINARY_NAME" "$(LOCALBIN)/" ;\ + cd "$(WORKSPACE_DIR)" ;\ + rm -rf "$$TEMP_DIR" ;\ if [ -L "$(1)" ]; then \ rm "$(1)" ;\ fi ;\ - ln -sf "$$(basename $(1))-$(3)-$(GOARCH)" "$(1)" ;\ + cd "$(LOCALBIN)" && ln -sf "$$BINARY_NAME" "$$BASE_NAME" ;\ } endef From 9c3ec9c1f50cf244357f0ce256e295e4820d9cee Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 13:13:02 +0900 Subject: [PATCH 23/27] build fix13 --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 28c5ca3..d541a35 100644 --- a/Makefile +++ b/Makefile @@ -325,9 +325,10 @@ define go-install-tool GO111MODULE=on go mod init tmp ;\ GO111MODULE=on go get $(2)@$(3) ;\ BASE_NAME=$$(basename $(1)) ;\ - ARCH_SUFFIX="-$(3)-$(GOARCH)" ;\ + HOST_ARCH=$$(go env GOARCH) ;\ + ARCH_SUFFIX="-$(3)-$$HOST_ARCH" ;\ BINARY_NAME="$$BASE_NAME$$ARCH_SUFFIX" ;\ - CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) \ + CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$$HOST_ARCH \ go build -o "$$BINARY_NAME" $(2) ;\ if [ -f "$(LOCALBIN)/$$BINARY_NAME" ]; then \ rm "$(LOCALBIN)/$$BINARY_NAME" ;\ From 2b030ad89817a4cb616c7ce28fbae3577bf937ca Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 13:17:21 +0900 Subject: [PATCH 24/27] build fix14 --- Makefile | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index d541a35..1bfb661 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ WORKSPACE_DIR ?= $(CURDIR) # Cross compilation settings GOOS ?= linux GOARCH ?= $(shell go env GOARCH) +HOST_ARCH ?= $(shell go env GOARCH) CGO_ENABLED ?= 0 COMMON_LDFLAGS ?= -s -w @@ -175,11 +176,11 @@ build: manifests generate fmt vet ## Build manager binary for current architectu build-only: ## Build manager binary without preprocessing steps @mkdir -p bin - @echo "Building $(GOARCH) binary..." + @echo "Building for GOARCH=$(GOARCH)..." GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) go build \ -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ -o bin/manager_$(GOARCH) cmd/main.go - @ln -sf manager_$(GOARCH) bin/manager + @cd bin && ln -sf manager_$$(go env GOARCH) manager build-all: manifests generate fmt vet ## Build manager binaries for all supported architectures. @mkdir -p bin @@ -319,16 +320,17 @@ define go-install-tool [ -f "$(1)" ] || { \ set -e ;\ mkdir -p $(LOCALBIN) ;\ - echo "Downloading and building $(2)@$(3)" ;\ + echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ GO111MODULE=on go get $(2)@$(3) ;\ BASE_NAME=$$(basename $(1)) ;\ - HOST_ARCH=$$(go env GOARCH) ;\ - ARCH_SUFFIX="-$(3)-$$HOST_ARCH" ;\ + BUILD_ARCH=$$(go env GOARCH) ;\ + ARCH_SUFFIX="-$(3)-$$BUILD_ARCH" ;\ BINARY_NAME="$$BASE_NAME$$ARCH_SUFFIX" ;\ - CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$$HOST_ARCH \ + echo "Building $$BINARY_NAME..." ;\ + CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$$BUILD_ARCH \ go build -o "$$BINARY_NAME" $(2) ;\ if [ -f "$(LOCALBIN)/$$BINARY_NAME" ]; then \ rm "$(LOCALBIN)/$$BINARY_NAME" ;\ @@ -336,10 +338,12 @@ define go-install-tool mv "$$BINARY_NAME" "$(LOCALBIN)/" ;\ cd "$(WORKSPACE_DIR)" ;\ rm -rf "$$TEMP_DIR" ;\ - if [ -L "$(1)" ]; then \ - rm "$(1)" ;\ + cd "$(LOCALBIN)" ;\ + if [ -L "$$BASE_NAME" ]; then \ + rm "$$BASE_NAME" ;\ fi ;\ - cd "$(LOCALBIN)" && ln -sf "$$BINARY_NAME" "$$BASE_NAME" ;\ + ln -sf "$$BINARY_NAME" "$$BASE_NAME" ;\ + echo "Created symlink: $$BASE_NAME -> $$BINARY_NAME" ;\ } endef From e1a706285d939c5768afbcd7339e4db6c8d1847a Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 13:24:21 +0900 Subject: [PATCH 25/27] build fix 15 --- Makefile | 158 ++++++++++++++++++++++++------------------------------- 1 file changed, 69 insertions(+), 89 deletions(-) diff --git a/Makefile b/Makefile index 1bfb661..f439ce2 100644 --- a/Makefile +++ b/Makefile @@ -1,36 +1,25 @@ # ==================== -# Tool Installation +# Base Settings # ==================== -# Tools will be installed to this directory -LOCALBIN ?= $(shell pwd)/bin -TOOLS_DIR := $(LOCALBIN) -WORKSPACE_DIR ?= $(CURDIR) +SHELL := /usr/bin/env bash +.SHELLFLAGS := -euo pipefail -c + +# Directory settings +WORKSPACE_DIR := $(CURDIR) +LOCALBIN := $(WORKSPACE_DIR)/bin -# Cross compilation settings +# Build settings GOOS ?= linux GOARCH ?= $(shell go env GOARCH) -HOST_ARCH ?= $(shell go env GOARCH) CGO_ENABLED ?= 0 -COMMON_LDFLAGS ?= -s -w - -# Get the workspace directory -WORKSPACE_DIR ?= $(shell pwd) - -# Version information -GIT_VERSION ?= $(shell git describe --tags --always) -FILE_VERSION ?= $(shell awk -F= '/^operator=/ {print $$2}' versions.txt) -# Use git version if available, otherwise fall back to versions.txt -VERSION ?= $(if $(shell git describe --tags --exact-match 2>/dev/null),$(GIT_VERSION),$(FILE_VERSION)) -VERSION_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') -VERSION_PKG ?= github.com/hellices/openapi-aggregator-operator/pkg/version - -# LDFLAGS for the build -COMMON_LDFLAGS ?= -s -w -OPERATOR_LDFLAGS ?= -X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -ifneq ($(origin CHANNELS), undefined) -BUNDLE_CHANNELS := --channels=$(CHANNELS) -endif +COMMON_LDFLAGS := -s -w + +# Version settings +VERSION ?= $(shell git describe --tags --exact-match 2>/dev/null || git describe --tags --always) +VERSION_DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') +VERSION_PKG := github.com/hellices/openapi-aggregator-operator/pkg/version +OPERATOR_LDFLAGS := -X $(VERSION_PKG).version=$(VERSION) -X $(VERSION_PKG).buildDate=$(VERSION_DATE) # DEFAULT_CHANNEL defines the default channel used in the bundle. # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") @@ -147,18 +136,15 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test test-e2e test-coverage -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \ - go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -v -race +test: manifests generate fmt vet envtest ## Run unit tests + KUBEBUILDER_ASSETS="$$($(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" \ + go test ./... -v -race -coverprofile=cover.out -covermode=atomic -# Run e2e tests using Kind -test-e2e: docker-build ## Run e2e tests against a Kind cluster - go test ./test/e2e/ -v -ginkgo.v +test-e2e: docker-build ## Run e2e tests + go test ./test/e2e/... -v -ginkgo.v -# Run tests with coverage report -test-coverage: test ## Run tests and generate coverage report +test-coverage: test ## Generate test coverage report go tool cover -html=cover.out -o coverage.html - @echo "Coverage report generated at coverage.html" .PHONY: lint lint: golangci-lint ## Run golangci-lint linter @@ -171,28 +157,27 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes ##@ Build .PHONY: build build-only build-all -build: manifests generate fmt vet ## Build manager binary for current architecture. +build: manifests generate fmt vet ## Build manager binary for current architecture @$(MAKE) build-only -build-only: ## Build manager binary without preprocessing steps - @mkdir -p bin - @echo "Building for GOARCH=$(GOARCH)..." - GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) go build \ - -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ - -o bin/manager_$(GOARCH) cmd/main.go - @cd bin && ln -sf manager_$$(go env GOARCH) manager - -build-all: manifests generate fmt vet ## Build manager binaries for all supported architectures. - @mkdir -p bin - @echo "Building amd64 binary..." - GOOS=$(GOOS) GOARCH=amd64 CGO_ENABLED=$(CGO_ENABLED) go build \ - -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ - -o bin/manager_amd64 cmd/main.go - @echo "Building arm64 binary..." - GOOS=$(GOOS) GOARCH=arm64 CGO_ENABLED=$(CGO_ENABLED) go build \ - -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" \ - -o bin/manager_arm64 cmd/main.go - @ln -sf manager_$$(go env GOARCH) bin/manager +build-only: ## Build manager binary without preprocessing + @mkdir -p $(LOCALBIN) + @echo "Building manager for $(GOARCH)..." + @CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build \ + -ldflags "$(COMMON_LDFLAGS) $(OPERATOR_LDFLAGS)" \ + -o $(LOCALBIN)/manager_$(GOARCH) cmd/main.go + @cd $(LOCALBIN) && ln -sf manager_$(GOARCH) manager + +BUILD_PLATFORMS := amd64 arm64 +build-all: manifests generate fmt vet ## Build manager binaries for all architectures + @mkdir -p $(LOCALBIN) + @for arch in $(BUILD_PLATFORMS); do \ + echo "Building manager for $$arch..." ;\ + GOOS=$(GOOS) GOARCH=$$arch CGO_ENABLED=0 go build \ + -ldflags "$(COMMON_LDFLAGS) $(OPERATOR_LDFLAGS)" \ + -o $(LOCALBIN)/manager_$$arch cmd/main.go ;\ + done + @cd $(LOCALBIN) && ln -sf manager_$$(go env GOARCH) manager .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. @@ -281,18 +266,24 @@ LOCALBIN ?= $(shell pwd)/bin bin_dir: ## Create bin directory mkdir -p $(LOCALBIN) -## Tool Binaries -KUBECTL ?= kubectl -KUSTOMIZE ?= $(LOCALBIN)/kustomize -CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen -ENVTEST ?= $(LOCALBIN)/setup-envtest -GOLANGCI_LINT = $(LOCALBIN)/golangci-lint - -## Tool Versions -KUSTOMIZE_VERSION ?= v5.4.3 -CONTROLLER_TOOLS_VERSION ?= v0.16.1 -ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.59.1 +## Tool configurations +TOOLS := kustomize controller-gen setup-envtest golangci-lint operator-sdk +TOOL_PATHS := $(addprefix $(LOCALBIN)/,$(TOOLS)) + +# Tool versions +KUSTOMIZE_VERSION := v5.4.3 +CONTROLLER_TOOLS_VERSION := v0.16.1 +ENVTEST_VERSION := release-0.19 +GOLANGCI_LINT_VERSION := v1.59.1 +OPERATOR_SDK_VERSION := v1.39.2 + +# Tool binaries +KUBECTL := kubectl +KUSTOMIZE := $(LOCALBIN)/kustomize +CONTROLLER_GEN := $(LOCALBIN)/controller-gen +ENVTEST := $(LOCALBIN)/setup-envtest +GOLANGCI_LINT := $(LOCALBIN)/golangci-lint +OPERATOR_SDK := $(LOCALBIN)/operator-sdk .PHONY: kustomize kustomize: bin_dir ## Download kustomize locally if necessary. @@ -312,38 +303,27 @@ envtest: ## Download envtest-setup locally if necessary golangci-lint: bin_dir ## Download golangci-lint locally if necessary. [ -f $(GOLANGCI_LINT) ] || $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) -# go-install-tool will build any package with custom target and name of binary, if it doesn't exist -# $1 - target path with name of binary -# $2 - package url which can be installed -# $3 - specific version of package +# Install Go tools +# params: binary-path package-url version define go-install-tool -[ -f "$(1)" ] || { \ +@[ -f "$(1)" ] || { \ set -e ;\ - mkdir -p $(LOCALBIN) ;\ - echo "Downloading and building $(2)@$(3) for $$(go env GOARCH)" ;\ + echo "Installing $(2)@$(3)..." ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ GO111MODULE=on go get $(2)@$(3) ;\ BASE_NAME=$$(basename $(1)) ;\ BUILD_ARCH=$$(go env GOARCH) ;\ - ARCH_SUFFIX="-$(3)-$$BUILD_ARCH" ;\ - BINARY_NAME="$$BASE_NAME$$ARCH_SUFFIX" ;\ - echo "Building $$BINARY_NAME..." ;\ - CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$$BUILD_ARCH \ - go build -o "$$BINARY_NAME" $(2) ;\ - if [ -f "$(LOCALBIN)/$$BINARY_NAME" ]; then \ - rm "$(LOCALBIN)/$$BINARY_NAME" ;\ - fi ;\ + BINARY_NAME="$$BASE_NAME-$(3)-$$BUILD_ARCH" ;\ + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$$BUILD_ARCH go build -o "$$BINARY_NAME" $(2) ;\ + mkdir -p $(LOCALBIN) ;\ mv "$$BINARY_NAME" "$(LOCALBIN)/" ;\ - cd "$(WORKSPACE_DIR)" ;\ - rm -rf "$$TEMP_DIR" ;\ - cd "$(LOCALBIN)" ;\ - if [ -L "$$BASE_NAME" ]; then \ - rm "$$BASE_NAME" ;\ - fi ;\ + cd $(LOCALBIN) ;\ ln -sf "$$BINARY_NAME" "$$BASE_NAME" ;\ - echo "Created symlink: $$BASE_NAME -> $$BINARY_NAME" ;\ + cd $(WORKSPACE_DIR) ;\ + rm -rf $$TEMP_DIR ;\ + echo "✓ Installed $$BASE_NAME" ;\ } endef From 8b6ae9b4ebc04033bb24a1c085bc6571ed0e69ba Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 14:11:17 +0900 Subject: [PATCH 26/27] build fix16 --- .github/workflows/build.yml | 44 +++++++++++-------- Makefile | 86 ++++++++++++++++++++++++------------- 2 files changed, 82 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dced612..b474161 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,17 +5,18 @@ on: branches: [ "main" ] env: - GO_VERSION: '1.22' + GO_VERSION: '1.23' + RUNNING_IN_CI: true jobs: build-and-test: + name: Build and Test runs-on: ubuntu-latest - strategy: - matrix: - arch: [amd64, arm64] steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 @@ -23,21 +24,28 @@ jobs: go-version: ${{ env.GO_VERSION }} cache: true - - name: Setup environment - run: | - mkdir -p bin - chmod 755 bin - echo "GOOS=linux" >> $GITHUB_ENV - echo "CGO_ENABLED=0" >> $GITHUB_ENV - echo "GOARCH=${{ matrix.arch }}" >> $GITHUB_ENV + - name: Cache tools + uses: actions/cache@v4 + with: + path: | + bin/ + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}-tools-${{ hashFiles('Makefile') }} + restore-keys: | + ${{ runner.os }}-go- - - name: Install dependencies - run: go mod download + - name: Install tools + run: | + echo "::group::Installing development tools" + make tools + echo "::endgroup::" - - name: Build and Test + - name: Build and test run: | - # Run tests first (includes necessary codegen) - make test + echo "::group::Building project" + make build + echo "::endgroup::" - # Build for current architecture - GOARCH=${{ matrix.arch }} make build-only + echo "::group::Running tests" + make test + echo "::endgroup::" diff --git a/Makefile b/Makefile index f439ce2..8e9d740 100644 --- a/Makefile +++ b/Makefile @@ -266,18 +266,23 @@ LOCALBIN ?= $(shell pwd)/bin bin_dir: ## Create bin directory mkdir -p $(LOCALBIN) -## Tool configurations +## Tool Versions and Configurations TOOLS := kustomize controller-gen setup-envtest golangci-lint operator-sdk -TOOL_PATHS := $(addprefix $(LOCALBIN)/,$(TOOLS)) - -# Tool versions -KUSTOMIZE_VERSION := v5.4.3 -CONTROLLER_TOOLS_VERSION := v0.16.1 -ENVTEST_VERSION := release-0.19 -GOLANGCI_LINT_VERSION := v1.59.1 -OPERATOR_SDK_VERSION := v1.39.2 +TOOL_VERSIONS := \ + KUSTOMIZE=v5.4.3 \ + CONTROLLER_GEN=v0.16.1 \ + ENVTEST=release-0.19 \ + GOLANGCI_LINT=v1.59.1 \ + OPERATOR_SDK=v1.39.2 + +# Build Architecture Settings +BUILD_ARCH ?= $(shell go env GOARCH) +ifeq ($(RUNNING_IN_CI),true) + # Force AMD64 in CI environment + BUILD_ARCH := amd64 +endif -# Tool binaries +# Tool Paths and URLs KUBECTL := kubectl KUSTOMIZE := $(LOCALBIN)/kustomize CONTROLLER_GEN := $(LOCALBIN)/controller-gen @@ -285,45 +290,66 @@ ENVTEST := $(LOCALBIN)/setup-envtest GOLANGCI_LINT := $(LOCALBIN)/golangci-lint OPERATOR_SDK := $(LOCALBIN)/operator-sdk -.PHONY: kustomize -kustomize: bin_dir ## Download kustomize locally if necessary. - [ -f $(KUSTOMIZE) ] || $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) +# Tool URLs +KUSTOMIZE_PKG := sigs.k8s.io/kustomize/kustomize/v5 +CONTROLLER_GEN_PKG := sigs.k8s.io/controller-tools/cmd/controller-gen +ENVTEST_PKG := sigs.k8s.io/controller-runtime/tools/setup-envtest +GOLANGCI_LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint + +.PHONY: tools tools-verify kustomize controller-gen envtest golangci-lint +tools: bin_dir ## Download and install all tools + @echo "Installing tools for $(BUILD_ARCH)..." + @$(MAKE) kustomize controller-gen envtest golangci-lint + @echo "All tools installed successfully!" + +tools-verify: ## Verify all required tools are installed + @echo "Verifying tools..." + @for tool in $(TOOLS); do \ + if [ ! -f "$(LOCALBIN)/$$tool" ]; then \ + echo "❌ Missing tool: $$tool" ;\ + exit 1 ;\ + fi ;\ + done + @echo "✓ All tools are installed" + +kustomize: bin_dir ## Install kustomize + $(call go-install-tool,$(KUSTOMIZE),$(KUSTOMIZE_PKG),v5.4.3) -.PHONY: controller-gen -controller-gen: bin_dir ## Download controller-gen locally if necessary. - [ -f $(CONTROLLER_GEN) ] || $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) +controller-gen: bin_dir ## Install controller-gen + $(call go-install-tool,$(CONTROLLER_GEN),$(CONTROLLER_GEN_PKG),v0.16.1) -.PHONY: envtest -envtest: ## Download envtest-setup locally if necessary - $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,release-0.19) - KUBEBUILDER_ASSETS=$$($(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path) \ - go test $(GO_TEST_FLAGS) $(shell go list ./... | grep -v /e2e) -coverprofile cover.out +envtest: bin_dir ## Install envtest + $(call go-install-tool,$(ENVTEST),$(ENVTEST_PKG),release-0.19) -.PHONY: golangci-lint -golangci-lint: bin_dir ## Download golangci-lint locally if necessary. - [ -f $(GOLANGCI_LINT) ] || $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) +golangci-lint: bin_dir ## Install golangci-lint + $(call go-install-tool,$(GOLANGCI_LINT),$(GOLANGCI_LINT_PKG),v1.59.1) # Install Go tools # params: binary-path package-url version define go-install-tool -@[ -f "$(1)" ] || { \ +@{ \ + if [ -f "$(1)" ]; then \ + echo "Tool already installed: $(1)" ;\ + exit 0 ;\ + fi ;\ set -e ;\ - echo "Installing $(2)@$(3)..." ;\ + echo "Installing $(2)@$(3) for $(BUILD_ARCH)..." ;\ TEMP_DIR=$$(mktemp -d) ;\ cd $$TEMP_DIR ;\ GO111MODULE=on go mod init tmp ;\ GO111MODULE=on go get $(2)@$(3) ;\ BASE_NAME=$$(basename $(1)) ;\ - BUILD_ARCH=$$(go env GOARCH) ;\ - BINARY_NAME="$$BASE_NAME-$(3)-$$BUILD_ARCH" ;\ - CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$$BUILD_ARCH go build -o "$$BINARY_NAME" $(2) ;\ + BINARY_NAME="$$BASE_NAME-$(3)-$(BUILD_ARCH)" ;\ + echo "Building $$BINARY_NAME..." ;\ + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(BUILD_ARCH) go build -o "$$BINARY_NAME" $(2) ;\ mkdir -p $(LOCALBIN) ;\ mv "$$BINARY_NAME" "$(LOCALBIN)/" ;\ cd $(LOCALBIN) ;\ + rm -f "$$BASE_NAME" ;\ ln -sf "$$BINARY_NAME" "$$BASE_NAME" ;\ cd $(WORKSPACE_DIR) ;\ rm -rf $$TEMP_DIR ;\ - echo "✓ Installed $$BASE_NAME" ;\ + echo "✓ Installed $$BASE_NAME for $(BUILD_ARCH)" ;\ } endef From ccef97f5262d443490cac5d6fa71352cb2fda06c Mon Sep 17 00:00:00 2001 From: hellices Date: Mon, 2 Jun 2025 14:19:48 +0900 Subject: [PATCH 27/27] build fix17 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8e9d740..ee393bc 100644 --- a/Makefile +++ b/Makefile @@ -138,7 +138,7 @@ vet: ## Run go vet against code. .PHONY: test test-e2e test-coverage test: manifests generate fmt vet envtest ## Run unit tests KUBEBUILDER_ASSETS="$$($(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" \ - go test ./... -v -race -coverprofile=cover.out -covermode=atomic + go test $(shell go list ./... | grep -v /test/e2e) -v -race -coverprofile=cover.out -covermode=atomic test-e2e: docker-build ## Run e2e tests go test ./test/e2e/... -v -ginkgo.v