package services import ( "context" "net/http" "github.com/gin-gonic/gin" "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/client" api "edge-infra.dev/pkg/edge/device-registrar/api/v1alpha1" config "edge-infra.dev/pkg/edge/device-registrar/config" ) // swagger:operation DELETE /applications/{appID} additional deleteApplication // --- // summary: Delete an external application. // description: Deletes an external application and removes it from all device bindings. // parameters: // - name: appID // in: path // description: ID of the external application to delete. // required: true // schema: // type: string // // responses: // // 204: // description: Application deleted successfully // 404: // description: Application not found // 500: // description: Internal server error func DeleteApplication(c *gin.Context) { appID := c.Param("appID") if appID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "external application name is required"}) return } k8sClient, ctx, cancel := config.GetClientandContext(c) defer cancel() // get external application extapp, err := getExternalApplicationByID(ctx, c, k8sClient, appID) if err != nil { if errors.IsNotFound(err) { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // go through device bindings and remove the external application from it's spec.applications err = removeApplicationFromDeviceBindings(ctx, k8sClient, extapp.Spec.ID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // now we can delete the external application err = k8sClient.Delete(ctx, extapp) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusNoContent, gin.H{}) } // GetDeviceBindingsForApplication retrieves all device bindings associated with an application, // then updates or deletes those who have the external application. func removeApplicationFromDeviceBindings(ctx context.Context, k8sClient client.Client, appID string) error { // retrieve all device bindings deviceBindingList := &api.DeviceBindingList{} err := k8sClient.List(ctx, deviceBindingList) if err != nil { return err } // go through each device binding and remove the application from it's spec.applications for i := range deviceBindingList.Items { device := deviceBindingList.Items[i] // Create a copy to avoid memory aliasing // see if the application is in the device binding's applications for _, extApp := range device.Spec.Applications { if extApp.ID == appID { //found err = updateOrDeleteDeviceBinding(ctx, k8sClient, device, appID) if err != nil { return err } } } } return nil } // updateOrDeleteDeviceBinding updates or deletes a device binding based on the number of applications it has. func updateOrDeleteDeviceBinding(ctx context.Context, k8sClient client.Client, device api.DeviceBinding, appID string) error { // if length of device.Spec.Applications is 1, then remove the device binding if len(device.Spec.Applications) == 1 { err := k8sClient.Delete(ctx, &device) if err != nil { return err } return nil } // otherwise, update the device binding to remove it device.Spec.Applications = removeApplication(device.Spec.Applications, appID) err := k8sClient.Update(ctx, &device) if err != nil { return err } return nil } // removeApplication removes an application from a list of applications func removeApplication(apps []api.ApplicationSubject, appID string) []api.ApplicationSubject { var newApps []api.ApplicationSubject for _, app := range apps { if app.ID != appID { newApps = append(newApps, app) } } return newApps }