diff --git a/cmd/maestro/server/grpc_server.go b/cmd/maestro/server/grpc_server.go index 88a3b0da..6b66531b 100644 --- a/cmd/maestro/server/grpc_server.go +++ b/cmd/maestro/server/grpc_server.go @@ -318,8 +318,6 @@ func decodeResourceSpec(evt *ce.Event) (*api.Resource, error) { return nil, fmt.Errorf("failed to convert cloudevent to resource payload: %v", err) } resource.Payload = payload - // set the resource type to bundle from grpc source - resource.Type = api.ResourceTypeBundle return resource, nil } @@ -337,6 +335,7 @@ func encodeResourceStatus(resource *api.Resource) (*ce.Event, error) { return nil, err } + // fill the resource status with resource payload if len(resource.Payload) > 0 { specEvt, err := api.JSONMAPToCloudEvent(resource.Payload) if err != nil { diff --git a/cmd/maestro/server/routes.go b/cmd/maestro/server/routes.go index 8cbaad36..d0c3d28d 100755 --- a/cmd/maestro/server/routes.go +++ b/cmd/maestro/server/routes.go @@ -23,6 +23,7 @@ func (s *apiServer) routes() *mux.Router { } resourceHandler := handlers.NewResourceHandler(services.Resources(), services.Generic()) + resourceBundleHandler := handlers.NewResourceBundleHandler(services.Resources(), services.Generic()) consumerHandler := handlers.NewConsumerHandler(services.Consumers(), services.Resources(), services.Generic()) errorsHandler := handlers.NewErrorsHandler() @@ -71,20 +72,18 @@ func (s *apiServer) routes() *mux.Router { apiV1ErrorsRouter.HandleFunc("", errorsHandler.List).Methods(http.MethodGet) apiV1ErrorsRouter.HandleFunc("/{id}", errorsHandler.Get).Methods(http.MethodGet) - // /api/maestro/v1/resources + // /api/maestro/v1/resources apiV1ResourceRouter := apiV1Router.PathPrefix("/resources").Subrouter() apiV1ResourceRouter.HandleFunc("", resourceHandler.List).Methods(http.MethodGet) apiV1ResourceRouter.HandleFunc("/{id}", resourceHandler.Get).Methods(http.MethodGet) - apiV1ResourceRouter.HandleFunc("", resourceHandler.Create).Methods(http.MethodPost) - apiV1ResourceRouter.HandleFunc("/{id}", resourceHandler.Patch).Methods(http.MethodPatch) apiV1ResourceRouter.HandleFunc("/{id}", resourceHandler.Delete).Methods(http.MethodDelete) apiV1ResourceRouter.Use(authMiddleware.AuthenticateAccountJWT) apiV1ResourceRouter.Use(authzMiddleware.AuthorizeApi) - // /api/maestro/v1/resource-bundles + // /api/maestro/v1/resource-bundles (deprecated) apiV1ResourceBundleRouter := apiV1Router.PathPrefix("/resource-bundles").Subrouter() - apiV1ResourceBundleRouter.HandleFunc("", resourceHandler.ListBundle).Methods(http.MethodGet) - apiV1ResourceBundleRouter.HandleFunc("/{id}", resourceHandler.GetBundle).Methods(http.MethodGet) + apiV1ResourceBundleRouter.HandleFunc("", resourceBundleHandler.List).Methods(http.MethodGet) + apiV1ResourceBundleRouter.HandleFunc("/{id}", resourceBundleHandler.Get).Methods(http.MethodGet) apiV1ResourceBundleRouter.Use(authMiddleware.AuthenticateAccountJWT) apiV1ResourceBundleRouter.Use(authzMiddleware.AuthorizeApi) diff --git a/data/generated/openapi/openapi.go b/data/generated/openapi/openapi.go index 07dce32c..5e03c836 100755 --- a/data/generated/openapi/openapi.go +++ b/data/generated/openapi/openapi.go @@ -77,7 +77,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _openapiYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5c\x5f\x8f\xdb\xb8\x11\x7f\xdf\x4f\x31\x40\x5b\x38\x39\xec\xda\x4e\xef\x0a\xb4\x46\x72\x40\x72\xbd\x14\x77\xc8\x25\x69\x36\x69\x1f\x8a\xc2\x4b\x93\x23\x8b\x89\x44\x2a\x24\xb5\x59\xa7\xed\x77\x2f\x48\xea\xbf\x25\xad\xec\xf3\xc6\xca\x9e\xf3\x92\x35\x35\x33\x9c\x21\x67\x7e\x1a\x0e\xc7\x96\x09\x0a\x92\xf0\x05\x7c\x3b\x9d\x4f\xe7\x67\x5c\x04\x72\x71\x06\x60\xb8\x89\x70\x01\x31\x41\x6d\x94\x84\x4b\x54\xd7\x9c\x22\x3c\x7d\xfd\xd3\x19\x00\x43\x4d\x15\x4f\x0c\x97\xa2\x8b\xe4\x1a\x95\x76\x8f\xe7\xd3\xf9\xf4\xd1\x99\x46\x65\x47\xac\xe4\x0b\x48\x55\xb4\x80\xd0\x98\x64\x31\x9b\x45\x92\x92\x28\x94\xda\x2c\xfe\x3c\x9f\xcf\xcf\x00\x1a\xd2\x69\xaa\x14\x0a\x03\x4c\xc6\x84\x8b\x3a\xbb\x5e\xcc\x66\x24\xe1\x53\x6b\x82\x0e\x79\x60\xa6\x54\xc6\xdb\x22\x7e\x21\x5c\xc0\x83\x44\x49\x96\x52\x3b\xf2\x10\xbc\x36\xed\xc2\xb4\x21\x6b\xbc\x4d\xe4\xa5\x21\x6b\x2e\xd6\xb9\xa0\x84\x98\xd0\xd9\x66\x25\xcc\xb2\x05\x99\x5d\x3f\x9a\x29\xd4\x32\x55\x14\xdd\x43\x80\x35\x1a\xff\x07\x80\x4e\xe3\x98\xa8\xcd\x02\xde\xa0\x49\x95\xd0\x40\x20\xe2\xda\x80\x0c\xa0\x60\xca\x49\x91\xa6\x8a\x9b\x4d\xce\x6a\xd5\x7e\x86\x44\xa1\x5a\xc0\xbf\xfe\x9d\x0d\x2a\xd4\x89\x14\x3a\x9f\xc9\xfe\x9b\xfc\x71\x3e\x9f\x94\x1f\x1b\x26\x3c\x85\x9f\x2f\x5f\xbd\x04\xa2\x14\xd9\x54\x67\x05\xb9\x7a\x8f\xd4\xe8\x0a\x1f\x95\xc2\xa0\x30\x55\x51\x00\x24\x49\x22\x4e\x89\x15\x36\x7b\xaf\xa5\xa8\x3f\x05\xd0\x34\xc4\x98\x34\x47\x01\x7e\xaf\x30\x58\xc0\xe4\x77\x33\x2a\xe3\x44\x0a\x14\x46\xcf\x3c\xad\x9e\xbd\xc9\x74\x78\xc1\xb5\x99\x94\x76\x7c\x37\x7f\xd4\x63\x47\x6a\x42\x30\xf2\x03\x0a\xe0\x1a\xb8\xb8\x26\x11\x67\xc7\x50\xfe\x47\xa5\xa4\xaa\x69\xfd\x6d\xb7\xd6\xef\x04\x49\x4d\x28\x15\xff\x8c\x0c\x8c\x84\x04\x55\x20\x55\x0c\x32\x41\xe5\xd4\x1a\x83\x05\x7f\xea\xf3\x9f\x77\x02\x6f\x12\xa4\x06\x19\xa0\xe5\x03\x49\x5d\xac\x1e\x7f\xed\x13\xa2\x48\x8c\x26\x83\x1b\x70\xf1\xd2\xc6\x5c\xd2\xcd\x12\xb2\xc6\xc9\x50\x62\xcd\x3f\xef\x40\x8c\x44\xd1\x70\x30\xb9\x54\x0c\xd5\xb3\xcd\x60\xfa\x80\x63\xc4\xb4\x27\x4f\x2c\x8a\x36\xe1\xe5\x07\x85\xc4\x20\x10\x10\xf8\xa9\x88\xf1\xdd\x80\xe5\x63\x8a\xda\x3c\x93\xac\x42\x57\xf3\x84\x3c\x6a\x81\x11\x43\x0a\x12\xcb\xc7\x15\xb2\x05\x18\x95\xe2\x59\x8f\x4b\xf4\x3b\x44\xbb\x3b\x0c\x41\x91\x49\x2f\x34\xf6\x40\x8a\x5f\xb3\xa3\x38\x72\x53\x77\x87\x23\x3d\x51\xf8\x0f\x8b\x76\x4e\x05\x1f\x85\x7a\x3c\x61\x78\x02\xee\x23\x5a\xf0\x97\x6e\x0b\x8a\x70\x25\x91\x42\xc2\x36\x80\x37\x5c\x1f\xe7\x7d\xbf\xd3\x0b\xe7\xa9\x80\xb4\xeb\x9d\x03\xd4\x86\xac\xcd\xc8\x4c\x88\x4d\x98\x3b\x8e\x49\x9d\xa9\xe0\xec\x3f\x9c\xfd\xaf\x3b\x1f\xfc\x1b\x1a\x20\xa2\x4c\xc7\x56\x1b\x28\xc2\xe2\x6e\x32\xc1\xc2\x21\x02\x99\x0a\x56\x9b\xf0\x8b\x2e\x5d\x2b\xf6\x9d\x00\xe4\x38\x16\x7c\xd7\x6d\xc1\x4b\x59\x7a\xe7\x27\x6e\x42\xd0\x09\x52\x1e\x70\x64\xc0\xd9\xd7\x82\x26\x63\x4d\x5f\x13\x62\x68\xb8\x05\x0a\xef\x12\xe6\xb2\x38\x71\x47\x29\x9c\x97\xcf\xca\x7d\x1d\x59\x2a\xf7\xda\xae\xca\x1b\x6f\x46\x7f\x5a\x37\x04\xe7\xd2\xcc\x5a\x9d\x52\x8a\x5a\x07\x69\x14\x6d\x46\x03\x78\xa7\x64\xef\x0b\x6b\x7d\xc2\xea\x51\x18\x71\x0f\x33\xd6\xad\x77\x8c\x03\x1e\x9b\xa5\x8e\x22\x43\xb5\xda\x46\x68\x70\xeb\x6d\xf3\x57\x37\x0c\x64\xcf\x97\x4d\x1b\x2c\xf7\xb8\x68\x59\x3e\x70\xd3\x76\xc0\xf2\x09\x19\xbf\xbc\xd6\x27\x64\x1c\x81\x11\xbb\x21\x8c\x8b\xa1\x11\x21\x4c\xb3\x16\x7b\x6b\x41\x93\xb3\xbe\xc3\xf3\xc5\x2a\x15\x2c\xda\xef\x3a\x05\x32\xde\x3b\x3d\x4b\x77\xde\xaa\xf8\xc9\xc7\x70\xb9\xf2\xcc\x69\x72\xba\x62\x19\x05\x44\x7d\x95\x67\xd4\xdf\xea\x15\xcb\x6d\xa8\xb4\x6b\x65\xcf\x43\xc2\x17\x2c\xf0\x65\x33\x8e\xa4\xce\xe7\x81\xe8\x04\x42\x23\xb0\x60\x60\x9e\x94\xf9\xcf\xfd\x49\x97\xbe\x66\x40\x6d\xcf\x94\xa8\x14\x3a\x8d\x0b\x39\xc3\x52\xa4\x82\xe9\x8b\xe6\x46\xf9\xac\xc7\x4c\x8a\x7e\xc8\x74\x38\xa5\x43\xa3\x40\xa2\x7b\x13\xbd\x3b\x26\x44\x3b\xa6\x44\x3b\x27\x45\xbb\xa7\x45\x07\xef\x3d\xc9\xa3\x7d\x37\x88\xb9\xed\xe2\x22\x8f\xdf\xb1\x5c\x58\xe4\xfa\x7c\x8d\xbd\x27\x4d\xdd\x4f\x45\xb7\x13\x84\xef\x63\x41\x4f\x25\xbf\x08\xd7\xaf\xac\x92\x3f\xbc\xf7\xa4\x01\x73\xc7\x31\xa9\x33\x29\x1c\x76\x42\x2d\x12\xb3\xbb\x3f\x9a\x16\x0e\x71\xe4\x33\x69\x2b\xf6\x9d\x00\x64\x8c\xa7\xd1\xc2\x3b\x4f\xc7\xd0\x83\x17\xeb\xfb\x7b\x4f\xee\x26\x85\xcb\x7b\x4f\xe8\x48\x53\xb9\x83\xf4\x9e\x14\x38\x37\x96\xde\x93\x53\xb2\x37\x06\xad\x4f\x58\x3d\x0a\x23\xee\x61\xc6\xda\xdd\x7b\x32\x8a\x0c\xf5\xf6\xde\x93\xfd\x5e\x36\x3b\xf6\x9e\x94\xe5\x83\x53\xef\xc9\x09\x19\x0f\x6b\xc1\x3d\x40\xc6\x3d\x7b\x4f\x46\x82\x30\x7b\xde\xa9\x94\x4f\x2c\x5b\x8e\x3b\x97\x56\x7e\x0e\x2c\x19\xf0\x64\x52\xcd\x26\x41\xff\x1d\xe2\xb3\x8a\xde\xb8\x80\x95\x23\xcb\x06\xfd\x87\xe7\x52\xc5\xc4\x2c\xe0\xe7\x7f\xbe\x3d\xcb\x0d\xcc\x84\xbe\x72\xb7\x20\x6f\x30\x40\x85\x82\x62\x5d\xba\xbf\x22\xc9\x86\x12\x65\x5d\xdd\xf0\x2a\xce\x71\x56\x5d\x27\xcf\xa4\x8d\xe2\x62\x5d\x0c\x7f\xe0\xe2\x76\xa2\xd0\x2e\x50\x1f\xd1\x0b\x5e\x56\x7a\x07\xea\x36\x68\xe2\x84\xac\x71\x9b\x88\x0b\x83\xeb\x8a\x27\x69\xfe\x79\x00\x95\x91\x86\x44\xb7\x91\x15\x27\x8b\xca\x1b\xc5\x6a\x5a\xf9\x68\x75\xaa\x7c\xb4\x93\x57\x3e\xba\x59\x2a\x9f\xb9\xc1\xd8\x87\xad\x73\xc2\x5c\x2e\x89\xa2\x57\x41\xbf\x07\xe6\xce\xdb\x70\x81\xb2\x45\xa1\x65\xa1\xdb\x97\xda\x46\x1a\xc3\x7a\xc8\xb4\x2e\xb7\xb5\x9f\x6c\xc5\x5c\x07\x69\x81\xac\xcb\xba\x9b\xb5\x30\x38\xd3\xab\x3e\xb2\x83\xf9\xd5\x4b\xb8\x9d\x6c\x76\x2b\xdf\xa6\x98\xbb\x6b\xac\x8d\xb7\x90\x0e\x06\x94\xbc\x71\xe1\x48\x3b\x2b\x48\x3c\x6c\x67\x73\xfc\x5d\x0e\xe6\xc8\x7f\xad\xa1\x85\xb6\x19\x5b\xe0\xeb\x9d\xc8\x96\xc4\x0c\x92\x0d\x10\x64\xa0\x67\x4f\xbe\x17\x86\xc7\xd5\xa6\xc4\xec\x3c\x7c\x18\x61\x59\x16\x77\x18\x61\x31\x11\x3c\x40\xdd\x2a\xaa\xb1\x5f\x00\x6b\x25\xd3\x64\xa9\x1a\x0e\xd2\xcb\xe2\x95\x5d\x4a\xff\x3a\x1d\xc2\xe1\xd7\x6a\xa9\x8d\x22\x06\xd7\x9b\x41\x3c\xda\x10\x93\xb6\xc6\x46\x85\xb4\xfa\xbb\x0b\xf7\x25\x6e\xeb\x5f\xae\x69\xfb\x22\xd1\x8e\x6f\xb1\x96\x18\x69\x8f\x90\x36\xc7\x69\x5d\x94\x4e\x0f\x68\xa5\xee\xd9\xfd\xce\x0d\x2d\x7b\x3d\xef\xdb\xb6\x56\x9b\xc7\xea\x63\x27\x74\xfe\x4d\xa0\x33\x1a\xc2\x88\x21\x83\x40\x30\x8f\xc8\x5f\xe3\x94\x07\x43\xf1\x5c\x99\x25\x95\x22\xe0\xeb\x3b\xd0\x69\x10\xe6\xe7\xa5\x8f\xd6\x70\xd9\x33\x60\x3a\x43\xa6\x2b\x68\xda\xc2\xa6\xc7\x21\x22\xb2\xc2\x68\xe8\x2a\x38\xa3\x18\xe3\x76\x63\x48\xf4\xba\x63\xfe\xde\xf9\xba\x62\xa9\x87\xa5\xdf\x6b\xbb\x23\x6a\x0f\x91\xd5\xde\xb5\xbd\x76\xb1\xde\xf4\xb6\xf3\xd6\xf5\xb8\xe4\xb6\xff\x76\x90\xef\x72\x49\xd1\x76\x21\xb3\xe3\x3b\x7c\xdb\x81\x3a\x6c\xbe\xdd\x71\x1a\xdb\xd5\x2c\x36\x94\x07\x25\xe7\xe1\xe5\xb5\x36\x17\x0b\x48\x88\x09\xb3\x8f\xb5\x92\xca\xdb\x10\x81\x33\xff\xbd\x11\x2a\x55\xce\xd2\x7a\x07\xd6\x2c\x8e\x6c\xb9\x4f\xf5\x40\xed\x75\xa8\x1c\x67\xad\x16\x1f\x53\x54\x9b\x36\x35\x5e\x93\x35\x82\x48\xe3\x15\xaa\x52\x17\xdf\x2c\xfa\x29\x44\x51\x1b\xc0\x1b\x8a\xc8\x74\xa5\x82\x65\x67\xa9\x1e\x95\xdb\x15\x6d\xbe\xb8\x18\x06\x24\x8d\xcc\x02\x1e\x95\x79\x14\x17\x3c\x4e\xe3\x72\xa8\x5c\x87\x80\x44\xda\xcb\xaf\x16\x04\xbc\x95\x95\xa9\x7b\xad\xfc\x85\xdc\x58\xf1\x5b\x86\x6a\x30\x12\x94\xeb\x91\xdd\xd3\x82\xec\x77\xec\x6a\x36\xcc\xfb\x6c\x70\xbd\x7a\x0d\x2b\xdc\x58\x87\x1d\x6d\x42\x1a\xd6\xfd\xf7\xa2\xd0\xe1\x32\xdb\x1a\xed\x1a\x54\xbc\x60\xa0\x8a\x1b\x54\x9c\x4c\x9d\xd3\xe9\x8d\x30\xe4\xc6\xae\x81\x09\xb9\x2e\x9d\x19\x78\x59\x87\xd4\x3c\xe6\x11\x51\x76\x75\x4c\x83\x05\x61\xf9\x29\x44\x85\x4b\xa0\x11\x49\x35\xda\x51\x22\xe0\xf2\xef\x2f\xdc\xbb\x08\x63\x14\xe6\xbc\x4c\x64\x75\xde\x2c\x63\x4d\xd5\xb9\x88\xf7\x5a\x0a\x20\xc6\x28\xbe\x4a\x0d\x6a\x98\x01\x95\x51\x1a\x8b\x3a\x15\xa1\x54\xa6\xc2\x4c\xa1\x10\xf7\x5c\x2a\xc0\x1b\x12\x27\x11\x9e\x03\x17\xe0\x1a\x19\xb3\x3d\x54\x1c\xaf\xd1\x82\x62\x95\x57\xfb\x9a\x2b\x81\x54\xa3\xb2\xc2\x4b\x13\x0d\x51\xae\x82\xe9\x08\xae\xe2\xcd\xd5\xe2\xac\x78\x78\x75\x75\xa5\x3f\x46\x15\x2b\x3c\x33\x44\xfc\x03\xc2\x24\xde\xfc\x61\x52\x25\x2d\xf9\xde\x6e\x2f\x3a\x50\x22\x80\x44\x5a\xc2\x0a\x7d\x15\x14\x19\x48\x1b\x58\x51\xed\x67\x18\xa6\x7b\x18\xa9\xd3\x55\xe1\x06\xda\x03\x1e\xba\xc6\x9a\xab\x40\xca\x27\x2b\xa2\xae\xce\x3b\x6d\xaa\xf2\x2e\x3d\x56\x4e\x3f\xe0\x06\x9e\xc0\x24\x90\x72\x02\x44\xb0\x56\x9a\x6b\x12\xa5\x68\xa9\x56\x44\x75\xac\xc2\x4f\x7e\xfb\xaa\x9e\x25\x26\xc6\x82\xf4\x35\x67\xc8\xce\x41\x2a\xe0\x9e\xc6\x4b\xe3\x1a\x30\x4e\xcc\xe6\xdc\x8e\x95\x25\xfd\xad\xbd\x34\x21\x31\x6e\xc4\x6e\x08\x84\x44\x43\x82\x2a\xe6\xda\x66\xcc\x76\x81\x34\x22\x7c\xe2\x51\x04\xab\x72\x9f\x7d\x74\x23\x9b\x0e\xc5\xd2\xac\x39\xb6\x1e\xa2\xd9\xe0\x1d\xc4\xa8\xdf\xdd\xd5\xe6\xe0\x51\x9a\x0b\x1e\x16\xa8\xab\xd4\xec\x1c\xac\x8d\x30\xdd\xd1\x81\x8b\x5d\x75\x8f\xbd\xdf\xe6\x81\x36\x20\x14\x89\xa6\xed\xde\xf7\x4a\xed\x37\x27\x2c\x89\x60\x4b\x08\xb8\xd2\x06\x86\x2b\x71\xee\x39\x5e\xf6\xea\x74\xa8\x88\x10\x12\xf0\x26\x89\x38\xe5\xc6\x9b\xe0\x01\xcc\x79\x7c\x0e\x2e\x83\x1d\xdd\xf7\x74\xd7\xfd\xdc\x8f\x1d\xc6\xcd\x53\xa7\x8f\x76\xf7\xbb\x71\x4c\x2e\x34\x5a\xfb\x2d\xe6\xe5\xdf\x45\xf1\xb3\xd9\x5d\x5a\xe1\x56\xa0\x02\x3c\xf7\x8f\x65\x60\x81\xe8\x42\x1b\x95\x52\x93\x2a\x2b\x51\xb8\xc4\xc9\x65\x9e\xda\xee\x06\x3c\x2e\x9e\x7e\x3f\x7d\xec\xc4\x7e\x0f\x42\x1a\x57\xc8\x2e\x05\x3e\xd6\x26\x27\xfa\x06\x62\x24\x42\x3b\xaf\x70\xf4\x4e\x20\x14\x62\x0a\x9e\x1f\xbd\x23\x2f\xbc\x57\x13\x1a\xc2\x65\x05\x15\xad\xee\x6b\x34\xc0\xd9\xb9\xbb\x4e\x39\x87\x24\x22\xe2\x01\x67\x4e\xc7\x0f\x5c\xb0\x87\xee\x2f\x0f\x9e\xf0\xa0\x98\x4e\x3f\xac\x79\x57\xf1\xb7\xa4\xb1\x13\x58\x87\xf6\x8b\x8b\xd2\x75\x3c\xfb\x13\xce\xce\xdd\x84\x76\xbe\x29\x67\xfe\x7f\x3b\xe1\x79\x06\xd4\xdf\xd4\xb9\xd0\xd0\xf0\x85\x7b\xf2\xa4\xd6\x5d\x55\x4e\xde\xeb\x30\xff\x0f\x00\x00\xff\xff\x44\x7b\xe9\xbf\x3b\x58\x00\x00") +var _openapiYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5c\x5f\x8f\x1b\xb7\x11\x7f\xbf\x4f\x31\x40\x5b\xc8\x0e\xee\x24\xb9\x49\x81\x56\xb0\x03\xd8\x69\x5c\x24\x70\x6c\xd7\x67\xb7\x0f\x45\x71\x47\x91\xb3\x5a\xda\xbb\xe4\x9a\x9c\x3d\x9f\xdc\xf6\xbb\x17\x24\xf7\xaf\xb4\xda\x5b\xc9\x77\x96\x72\x51\x5e\x72\xcb\x1d\x0e\x67\xc8\xdf\xfc\x76\x86\xa4\xac\x33\x54\x2c\x93\x33\xf8\x76\x3c\x1d\x4f\x4f\xa4\x8a\xf4\xec\x04\x80\x24\x25\x38\x83\x94\xa1\x25\xa3\xe1\x1c\xcd\x95\xe4\x08\x4f\x5f\xff\x74\x02\x20\xd0\x72\x23\x33\x92\x5a\x6d\x12\xb9\x42\x63\xfd\xeb\xe9\x78\x3a\x7e\x74\x62\xd1\xb8\x16\xa7\xf9\x0c\x72\x93\xcc\x20\x26\xca\x66\x93\x49\xa2\x39\x4b\x62\x6d\x69\xf6\xe7\xe9\x74\x7a\x02\xb0\xa2\x9d\xe7\xc6\xa0\x22\x10\x3a\x65\x52\xb5\xbb\xdb\xd9\x64\xc2\x32\x39\x76\x2e\xd8\x58\x46\x34\xe6\x3a\x5d\x57\xf1\x0b\x93\x0a\x1e\x64\x46\x8b\x9c\xbb\x96\x87\x10\xac\xe9\x56\x66\x89\x2d\xf0\x26\x95\xe7\xc4\x16\x52\x2d\x4a\x45\x19\xa3\xd8\xfb\xe6\x34\x4c\x8a\x09\x99\x5c\x3d\x9a\x18\xb4\x3a\x37\x1c\xfd\x4b\x80\x05\x52\xf8\x03\xc0\xe6\x69\xca\xcc\x72\x06\x6f\x90\x72\xa3\x2c\x30\x48\xa4\x25\xd0\x11\x54\x9d\x4a\x51\xe4\xb9\x91\xb4\x2c\xbb\x3a\xb3\x9f\x21\x33\x68\x66\xf0\xaf\x7f\x17\x8d\x06\x6d\xa6\x95\x2d\x47\x72\xff\x8d\xfe\x38\x9d\x8e\xea\xc7\x15\x17\x9e\xc2\xcf\xe7\xaf\x5e\x02\x33\x86\x2d\x9b\xa3\x82\x9e\xbf\x47\x4e\xb6\xd1\x8f\x6b\x45\xa8\xa8\xa9\x0a\x80\x65\x59\x22\x39\x73\xca\x26\xef\xad\x56\xed\xb7\x00\x96\xc7\x98\xb2\xd5\x56\x80\xdf\x1b\x8c\x66\x30\xfa\xdd\x84\xeb\x34\xd3\x0a\x15\xd9\x49\x90\xb5\x93\x37\x85\x0d\x2f\xa4\xa5\x51\xed\xc7\x77\xd3\x47\x3d\x7e\xe4\x14\x03\xe9\x0f\xa8\x40\x5a\x90\xea\x8a\x25\x52\xec\xc3\xf8\x1f\x8d\xd1\xa6\x65\xf5\xb7\x9b\xad\x7e\xa7\x58\x4e\xb1\x36\xf2\x33\x0a\x20\x0d\x19\x9a\x48\x9b\x14\x74\x86\xc6\x9b\x75\x08\x1e\xfc\xa9\x0f\x3f\xef\x14\x5e\x67\xc8\x09\x05\xa0\xeb\x07\x9a\xfb\x58\xdd\xff\xdc\x67\xcc\xb0\x14\xa9\xa0\x1b\xf0\xf1\xd2\xd5\xb9\x96\x9b\x64\x6c\x81\xa3\xa1\xc2\x56\x7e\xde\x42\x18\x99\xe1\xf1\x60\x71\x6d\x04\x9a\x67\xcb\xc1\xf2\x91\xc4\x44\xd8\x51\x1f\xf5\x4c\xfe\x23\xc5\xff\x36\xf3\xcf\xdf\x90\x80\xd5\xd1\x3f\x5f\x42\x15\x3e\x77\x43\x3c\x65\x90\x43\xa4\x73\x25\x5a\x03\x7e\x55\xcc\x94\x76\x1c\x89\xe6\x00\x3c\xf8\x6e\xb3\x07\x2f\x75\x8d\xce\x4f\x92\x62\xb0\x19\x72\x19\x49\x14\x20\x05\xe0\xb5\xb4\xfb\xf9\x58\xfd\x76\xd9\x52\x8a\x51\x91\x12\x25\x48\xb8\xc6\x28\x7f\xf5\xcd\x0d\x52\xf9\x72\x3a\xe9\x81\x47\x45\x27\xc1\x1a\x01\x36\xe7\x1c\xad\x8d\xf2\x24\x59\x36\x21\xd6\xb3\x3a\xff\x70\xa1\xec\xe7\x38\xac\x8e\x3d\x9c\xe5\x39\xb2\xd2\x91\x95\xbe\xc4\x89\xed\x58\xc9\xc7\x90\x2b\x6c\x56\x42\x77\x7f\x2e\xec\x4c\x4f\x9b\xf2\xa1\xb3\x79\xae\x44\xd2\x51\x91\x09\xcc\x0c\x72\x46\x28\x66\x40\x26\xc7\xe1\x85\x1a\x14\x2a\xf7\x53\xaf\x85\xc1\x0f\xa1\x6c\x7b\xe6\x2d\x39\x16\x6f\x07\xc1\x5e\xbf\x9d\x74\xe4\x1e\x17\x6f\x25\x59\x75\xd7\x70\x37\x31\xd6\x6a\x69\x17\x88\xe2\x2b\x56\x78\xc5\x88\x07\x52\xe8\x05\x7a\x3a\x52\xd3\x01\x78\x30\x30\xb1\x2a\xf0\x73\x7f\xf2\xab\x5f\x33\xcd\x76\xa7\x55\x5c\x2b\x9b\xa7\x95\x9e\x61\x3b\xdc\x55\xa7\xaf\x9a\x31\x95\xa3\xee\x33\x55\xfa\xa1\xb0\xe1\x98\x24\x1d\x04\x13\xdd\x9b\xe8\xdd\x32\x4d\xda\x32\x51\xda\x3a\x55\xda\x3e\x59\xda\x2a\x5d\x02\xc8\xb4\x5d\x27\x9a\x1f\x0c\x32\xbf\xf1\xa4\xf0\x53\x15\xed\xdb\x51\xcc\xc7\x1c\x2d\x3d\xd3\xa2\x21\xd7\xc2\x44\x19\xbf\x20\x18\xb1\x4a\xc4\xf5\x93\x66\x25\x0d\xeb\x04\x47\x3f\x34\xba\x81\x31\x84\x4f\x46\xbd\x24\xd9\x43\x2e\x61\xce\xf6\x02\xe9\x55\xdb\x8f\xbb\x74\x47\x0a\xdf\xc5\x83\xbf\xf4\xa0\xbb\x0c\x57\x96\x18\x64\x62\xf9\x6b\x49\x1c\x9f\x2a\xc8\x37\x7d\x7d\x80\xbb\x90\x95\x6a\x01\x14\xe3\x2a\xcd\xed\xc7\xa5\x8d\x49\xe1\xa0\xb3\xc7\x2a\x2f\xbb\xfb\xca\xb4\xc2\xc3\x9e\x4b\xd2\x4e\xea\x3b\xf2\xc7\x21\x16\xa3\x15\x3a\x8f\x55\xe8\x2d\x5b\x9e\x31\xe2\xf1\x1a\x27\xbc\xcb\x84\x4f\xe2\xd4\x1d\x65\x70\x41\xbf\xa8\xd7\xf5\xc0\x32\xb9\xd7\x6e\x56\xde\x04\x37\xfa\xb3\xba\x21\x3c\x97\x17\xde\x76\x1e\x8a\xee\x9b\xf0\x8e\xb9\xde\x57\xb6\xfa\xc8\xd5\x07\xe1\xc4\x3d\x4c\x58\xd7\xbe\x31\x9e\x78\x5c\x92\x7a\x10\x09\xea\xcd\x77\x55\x76\xfb\xd8\x6c\x79\x57\xa5\xde\x3d\x38\xde\x55\x39\x32\xe3\xed\x7a\x70\x0f\x98\x71\xc7\xbb\x2a\x07\xc2\x30\x3b\x1e\xa9\xd4\x6f\x5c\xb7\x92\x77\xce\x9d\xfe\x92\x58\x0a\xe2\x29\xb4\xd2\x32\xc3\xf0\x93\x85\x93\x86\xdd\x38\x83\xb9\x17\x2b\x1a\xc3\xc3\x73\x6d\x52\x46\x33\xf8\xf9\x9f\x6f\x4f\x4a\x07\x0b\xa5\xaf\xfc\x21\xc8\x1b\x8c\xd0\xa0\xe2\xd8\xd6\x1e\x4e\x48\x8a\xa6\xcc\x38\xa8\x93\x6c\xf2\x9c\x14\xcd\x79\x0a\x9d\x2c\x19\xa9\x16\x55\xf3\x07\xa9\x6e\x16\x8a\xdd\x04\xf5\x09\xbd\x90\xf5\x46\xef\x40\xdb\x06\x0d\x9c\xb1\x05\xae\x0b\x49\x45\xb8\x68\x20\xc9\xca\xcf\x03\xa4\x48\x13\x4b\x6e\x12\xab\x2a\x8b\xc6\x17\xc5\x59\xda\x78\x74\x36\x35\x1e\xdd\xe0\x8d\x47\x3f\x4a\xe3\x59\x12\xa6\x21\x6c\x3d\x08\x4b\xbd\x2c\x49\x5e\x45\xfd\x08\x2c\xc1\xbb\x02\x81\xfa\xde\x42\xc7\x44\x77\x4f\xb5\x8b\x34\x81\xed\x90\xe9\x9c\x6e\xe7\x3f\x5b\x8b\xb9\x0d\xa2\x15\xb3\x5e\xb4\x61\xd6\xd1\xc1\xbb\xde\xc4\xc8\x16\xee\x37\xcf\xe0\xb6\xf2\xd9\xcf\x7c\x97\x61\xfe\xa8\xb1\xd5\xde\x21\x3a\x98\x50\xca\x7b\x0b\x7b\x5a\x59\xc5\xd2\x61\x2b\x5b\xf2\xef\xc5\xe0\x1e\xe5\x8f\xc3\x3a\x64\x57\x63\x0b\xc2\x76\x27\x8a\x0b\x46\x83\x74\x03\x44\x05\xe9\xb9\xca\xf7\x8c\x64\xda\xbc\xc4\x58\xd4\xc3\xb7\xa3\xac\xc8\xe2\x6e\x47\x59\x8a\xc4\x04\x23\xd6\xa5\x6a\x65\xbd\x00\x52\xa6\x64\x84\x96\xbe\x04\x85\x1b\x54\x07\xa7\x2e\x74\xf8\xec\x6e\x63\xcc\x05\xd7\x2a\x92\x8b\x3b\xb0\xc9\x12\xa3\xbc\x53\x6f\x43\xb4\xf9\xe3\xb1\xfb\xc2\x06\xed\xdf\xa8\xb4\x2f\x32\x1d\x69\xe1\x48\x0b\x47\x5a\x18\x4c\x0b\xf5\xe5\xe4\xfb\x46\x0e\xcd\x7b\x8d\xe5\x0e\x43\xa7\x8f\x3b\xd2\xc3\x46\x97\x37\x39\xdd\x45\x12\x3d\xf0\x4f\xd8\x1c\x93\xa1\x6b\xee\x9d\x12\x42\x3a\x18\xb2\xe4\xf5\x86\xf1\x7b\xc7\xdb\xc4\x1c\x3d\x5d\xfa\x63\x74\x33\x7f\xec\xa0\xb2\x79\x43\x6c\xa7\x55\x6c\x5f\x2d\xdb\x7a\xe9\x7a\x02\x70\x1d\xb3\x1b\xc4\xb7\x39\x0b\xe8\x3a\xf7\xd8\xb2\xe0\x5b\x07\xd0\x06\x9f\x6f\x06\xce\xca\x72\xad\xd6\xf4\x75\x3d\xe2\x11\x5e\x9f\x1e\x4b\x35\x83\x8c\x51\x5c\x3c\xb6\x76\x2e\xde\xc6\x08\x52\x84\xdf\x6c\x70\x6d\xca\x2e\x9d\x47\x4d\xab\x7b\x10\x6b\xf0\x69\xd6\xad\xc1\x86\x46\xd5\xe8\xac\xf8\x98\xa3\x59\x76\x99\xf1\x9a\x2d\x10\x54\x9e\xce\xd1\xd4\xb6\x84\x2b\x99\x9f\x62\x54\xad\x06\xbc\xe6\x88\xc2\x36\x36\x8a\xdc\x28\xcd\x8a\xb4\xdb\xd0\xd5\xcf\xb4\xc0\x88\xe5\x09\xcd\xe0\x51\xd5\x94\x4a\x25\xd3\x3c\xad\x9b\xea\x79\x88\x58\x62\x83\xfe\x66\xdd\x1d\xbc\x6c\x0c\xdd\xeb\xe5\x2f\xec\xda\xa9\x5f\x73\xd4\x02\x69\x30\xfe\x26\xea\x8e\x1e\x14\xff\x3a\x45\xcb\x87\x69\x9f\x0f\xfe\x46\xdc\x8a\x17\xbe\x6d\x83\x1f\x5d\x4a\x56\xbc\xfb\xef\x59\x65\xc3\x79\xb1\x34\xd6\x5f\x03\x09\x8a\x81\x1b\x49\x68\x24\x1b\x7b\xd0\xd9\xa5\x22\x76\xed\xe6\x80\x62\x69\x6b\x30\x83\xac\xb7\xfb\xac\x4c\x65\xc2\x8c\x9b\x1d\x5a\xe9\x82\x70\xf1\x29\x46\x83\x17\xc0\x13\x96\x5b\x74\xad\x4c\xc1\xf9\xdf\x5f\xf8\x2f\x2f\xa6\xa8\xe8\xb4\x52\x94\xdb\xf2\x4a\x8a\x73\xd5\x96\x2a\xde\x5b\xad\x80\x11\x19\x39\xcf\x09\x2d\x4c\x80\xeb\x24\x4f\x55\x5b\x8a\x71\xae\x73\x45\x63\xa8\xd4\x3d\xd7\x06\xf0\x9a\xa5\x59\x82\xa7\x20\x15\xf8\xeb\x82\xc5\x1a\x1a\x89\x57\xe8\x48\xb1\xd9\xd7\x86\xad\x4d\x06\xb9\x45\xe3\x94\xd7\x2e\x12\x33\x7e\xa3\xd0\x0b\x5c\xa6\xcb\xcb\xd9\x49\xf5\xf2\xf2\xf2\xd2\x7e\x4c\x1a\x5e\x84\xce\x90\xc8\x0f\x08\xa3\x74\xf9\x87\x51\x53\xb4\xee\xf7\x76\x7d\xd2\x81\x33\x05\x2c\xb1\x1a\xe6\x18\x36\x1b\x51\x80\x76\x81\x95\xf8\x33\xd8\xf2\x5a\xfb\x78\x07\x27\x6d\x3e\xaf\x60\x60\x03\xe1\xa1\xbf\xbf\x72\x19\x69\xfd\x64\xce\xcc\xe5\xe9\x46\x9f\x9a\x7d\x2f\x02\x57\x8e\x3f\xe0\x12\x9e\xc0\x28\xd2\x7a\x04\x4c\x89\x4e\x99\x2b\x96\xe4\xe8\xa4\xe6\xcc\x6c\x98\x85\x9f\xc2\xf2\x35\x91\xa5\x46\xe4\x48\xfa\x4a\x0a\x14\xa7\xa0\x0d\xc8\x20\x13\xb4\x49\x0b\x98\x66\xb4\x3c\x75\x6d\xf5\xce\xf9\xda\x5a\x52\xcc\xc8\xb7\xb8\x05\x81\x98\x59\xc8\xd0\xa4\xd2\xba\xfa\xc0\x4d\x90\x45\x84\x4f\x32\x49\x60\x5e\xaf\x73\x88\x6e\x14\xe3\xa1\x5c\x5a\x5c\x41\x6d\x87\x68\xd1\x78\x07\x31\x1a\x56\x77\xbe\xbc\xf5\x28\x2d\x15\x0f\x0b\xd4\x79\x4e\x5b\x07\xeb\x4a\x98\x6e\x09\xe0\x6a\x55\xfd\xeb\x80\xdb\x32\xd0\x06\x84\x22\xb3\xbc\x1b\x7d\xaf\xcc\x6e\x63\xc2\x05\x53\xe2\x02\x22\x69\x2c\xc1\x70\x23\x4e\x43\x8f\x97\xbd\x36\xdd\x56\x44\x28\x0d\x78\x9d\x25\x92\x4b\x0a\x2e\x04\x02\xf3\x88\x2f\xc9\x65\x30\xd0\xc3\xcd\xe9\x36\xce\x43\xdb\xed\xc0\x3c\xf7\xf6\x58\x7f\x8c\x9a\xa6\xec\xcc\xa2\xf3\xdf\x71\x5e\xf9\x8b\x8f\x30\x9a\x5b\xa5\x39\xae\x05\x2a\xc0\xf3\xf0\x5a\x47\x8e\x88\xce\x2c\x99\x9c\x53\x6e\x9c\x46\xe5\x13\x27\x9f\x79\x5a\xb7\x1a\xf0\xb8\x7a\xfb\xfd\xf8\xb1\x57\xfb\x3d\x28\x4d\x7e\xbf\xb8\x56\xf8\xd8\x52\x29\xf4\x0d\xa4\xc8\x94\xf5\xa8\xf0\xf2\x5e\x21\x54\x6a\xaa\x3e\x3f\x06\x20\xcf\x02\xaa\x19\x8f\xe1\xbc\xc1\x8a\xce\xf6\x05\x12\x48\x71\xea\x4f\x2d\x4e\x21\x4b\x98\x7a\x20\x85\xb7\xf1\x83\x54\xe2\xa1\xff\x2b\x90\x27\x3c\xa8\x86\xb3\x0f\x5b\xe8\xaa\xfe\xd6\x3c\xf5\x0a\xdb\xd4\x7e\x76\x56\x43\x27\x74\x7f\x22\xc5\xa9\x1f\xd0\x8d\x37\x96\x22\xfc\xdf\x0d\x78\x5a\x10\xf5\x37\xed\x5e\x48\x3c\x7e\xe1\xdf\x3c\x69\x5d\x62\xaa\x07\xef\x05\xcc\xff\x03\x00\x00\xff\xff\xd0\xe0\x4c\x0d\x11\x4c\x00\x00") func openapiYamlBytes() ([]byte, error) { return bindataRead( @@ -92,7 +92,7 @@ func openapiYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "openapi.yaml", size: 22587, mode: os.FileMode(493), modTime: time.Unix(1737424969, 0)} + info := bindataFileInfo{name: "openapi.yaml", size: 19473, mode: os.FileMode(493), modTime: time.Unix(1739961042, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index ec16eb32..0812c505 100755 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -47,57 +47,9 @@ paths: - $ref: '#/components/parameters/search' - $ref: '#/components/parameters/orderBy' - $ref: '#/components/parameters/fields' - post: - summary: Create a new resource - security: - - Bearer: [] - requestBody: - description: Resource data - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Resource' - responses: - '201': - description: Created - content: - application/json: - schema: - $ref: '#/components/schemas/Resource' - '400': - description: Validation errors occurred - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Auth token is invalid - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Unauthorized to perform operation - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: Resource already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: An unexpected error occurred creating the resource - content: - application/json: - schema: - $ref: '#/components/schemas/Error' /api/maestro/v1/resources/{id}: get: - summary: Get an resource by id + summary: Get a resource by id security: - Bearer: [] responses: @@ -131,60 +83,8 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - patch: - summary: Update an resource - security: - - Bearer: [] - requestBody: - description: Updated resource data - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ResourcePatchRequest' - responses: - '200': - description: Resource updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Resource' - '400': - description: Validation errors occurred - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Auth token is invalid - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Unauthorized to perform operation - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '404': - description: No resource with specified id exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '409': - description: Resource already exists - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Unexpected error updating resource - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + parameters: + - $ref: '#/components/parameters/id' delete: summary: Delete a resource security: @@ -222,10 +122,11 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - parameters: - - $ref: '#/components/parameters/id' + parameters: + - $ref: '#/components/parameters/id' /api/maestro/v1/resource-bundles: get: + deprecated: true summary: Returns a list of resource bundles security: - Bearer: [] @@ -262,7 +163,8 @@ paths: - $ref: '#/components/parameters/fields' /api/maestro/v1/resource-bundles/{id}: get: - summary: Get an resource bundle by id + deprecated: true + summary: Get a resource bundle by id security: - Bearer: [] responses: @@ -384,7 +286,7 @@ paths: $ref: '#/components/schemas/Error' /api/maestro/v1/consumers/{id}: get: - summary: Get an consumer by id + summary: Get a consumer by id security: - Bearer: [] responses: @@ -584,14 +486,18 @@ components: deleted_at: type: string format: date-time - manifest: - type: object - group_resource: + metadata: type: object + manifests: + type: array + items: + type: object delete_option: type: object - update_strategy: - type: object + manifest_configs: + type: array + items: + type: object status: type: object ResourceList: @@ -603,26 +509,6 @@ components: type: array items: $ref: '#/components/schemas/Resource' - ResourcePatchRequest: - type: object - properties: - version: - type: integer - manifest: - type: object - delete_option: - type: object - update_strategy: - type: object - ResourceBundleList: - allOf: - - $ref: '#/components/schemas/List' - - type: object - properties: - items: - type: array - items: - $ref: '#/components/schemas/ResourceBundle' ResourceBundle: allOf: - $ref: '#/components/schemas/ObjectReference' @@ -657,6 +543,15 @@ components: type: object status: type: object + ResourceBundleList: + allOf: + - $ref: '#/components/schemas/List' + - type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/ResourceBundle' Consumer: allOf: - $ref: '#/components/schemas/ObjectReference' diff --git a/pkg/api/openapi/.openapi-generator/FILES b/pkg/api/openapi/.openapi-generator/FILES index e19f8922..fe412deb 100644 --- a/pkg/api/openapi/.openapi-generator/FILES +++ b/pkg/api/openapi/.openapi-generator/FILES @@ -21,12 +21,10 @@ docs/ObjectReference.md docs/Resource.md docs/ResourceAllOf.md docs/ResourceBundle.md -docs/ResourceBundleAllOf.md docs/ResourceBundleList.md docs/ResourceBundleListAllOf.md docs/ResourceList.md docs/ResourceListAllOf.md -docs/ResourcePatchRequest.md git_push.sh go.mod go.sum @@ -44,12 +42,10 @@ model_object_reference.go model_resource.go model_resource_all_of.go model_resource_bundle.go -model_resource_bundle_all_of.go model_resource_bundle_list.go model_resource_bundle_list_all_of.go model_resource_list.go model_resource_list_all_of.go -model_resource_patch_request.go response.go test/api_default_test.go utils.go diff --git a/pkg/api/openapi/README.md b/pkg/api/openapi/README.md index 04e6c4ee..fca2ba78 100644 --- a/pkg/api/openapi/README.md +++ b/pkg/api/openapi/README.md @@ -79,16 +79,14 @@ Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- *DefaultApi* | [**ApiMaestroV1ConsumersGet**](docs/DefaultApi.md#apimaestrov1consumersget) | **Get** /api/maestro/v1/consumers | Returns a list of consumers *DefaultApi* | [**ApiMaestroV1ConsumersIdDelete**](docs/DefaultApi.md#apimaestrov1consumersiddelete) | **Delete** /api/maestro/v1/consumers/{id} | Delete a consumer -*DefaultApi* | [**ApiMaestroV1ConsumersIdGet**](docs/DefaultApi.md#apimaestrov1consumersidget) | **Get** /api/maestro/v1/consumers/{id} | Get an consumer by id +*DefaultApi* | [**ApiMaestroV1ConsumersIdGet**](docs/DefaultApi.md#apimaestrov1consumersidget) | **Get** /api/maestro/v1/consumers/{id} | Get a consumer by id *DefaultApi* | [**ApiMaestroV1ConsumersIdPatch**](docs/DefaultApi.md#apimaestrov1consumersidpatch) | **Patch** /api/maestro/v1/consumers/{id} | Update an consumer *DefaultApi* | [**ApiMaestroV1ConsumersPost**](docs/DefaultApi.md#apimaestrov1consumerspost) | **Post** /api/maestro/v1/consumers | Create a new consumer *DefaultApi* | [**ApiMaestroV1ResourceBundlesGet**](docs/DefaultApi.md#apimaestrov1resourcebundlesget) | **Get** /api/maestro/v1/resource-bundles | Returns a list of resource bundles -*DefaultApi* | [**ApiMaestroV1ResourceBundlesIdGet**](docs/DefaultApi.md#apimaestrov1resourcebundlesidget) | **Get** /api/maestro/v1/resource-bundles/{id} | Get an resource bundle by id +*DefaultApi* | [**ApiMaestroV1ResourceBundlesIdGet**](docs/DefaultApi.md#apimaestrov1resourcebundlesidget) | **Get** /api/maestro/v1/resource-bundles/{id} | Get a resource bundle by id *DefaultApi* | [**ApiMaestroV1ResourcesGet**](docs/DefaultApi.md#apimaestrov1resourcesget) | **Get** /api/maestro/v1/resources | Returns a list of resources *DefaultApi* | [**ApiMaestroV1ResourcesIdDelete**](docs/DefaultApi.md#apimaestrov1resourcesiddelete) | **Delete** /api/maestro/v1/resources/{id} | Delete a resource -*DefaultApi* | [**ApiMaestroV1ResourcesIdGet**](docs/DefaultApi.md#apimaestrov1resourcesidget) | **Get** /api/maestro/v1/resources/{id} | Get an resource by id -*DefaultApi* | [**ApiMaestroV1ResourcesIdPatch**](docs/DefaultApi.md#apimaestrov1resourcesidpatch) | **Patch** /api/maestro/v1/resources/{id} | Update an resource -*DefaultApi* | [**ApiMaestroV1ResourcesPost**](docs/DefaultApi.md#apimaestrov1resourcespost) | **Post** /api/maestro/v1/resources | Create a new resource +*DefaultApi* | [**ApiMaestroV1ResourcesIdGet**](docs/DefaultApi.md#apimaestrov1resourcesidget) | **Get** /api/maestro/v1/resources/{id} | Get a resource by id ## Documentation For Models @@ -107,12 +105,10 @@ Class | Method | HTTP request | Description - [Resource](docs/Resource.md) - [ResourceAllOf](docs/ResourceAllOf.md) - [ResourceBundle](docs/ResourceBundle.md) - - [ResourceBundleAllOf](docs/ResourceBundleAllOf.md) - [ResourceBundleList](docs/ResourceBundleList.md) - [ResourceBundleListAllOf](docs/ResourceBundleListAllOf.md) - [ResourceList](docs/ResourceList.md) - [ResourceListAllOf](docs/ResourceListAllOf.md) - - [ResourcePatchRequest](docs/ResourcePatchRequest.md) ## Documentation For Authorization diff --git a/pkg/api/openapi/api/openapi.yaml b/pkg/api/openapi/api/openapi.yaml index 4fbc9439..3dc85369 100644 --- a/pkg/api/openapi/api/openapi.yaml +++ b/pkg/api/openapi/api/openapi.yaml @@ -121,54 +121,6 @@ paths: security: - Bearer: [] summary: Returns a list of resources - post: - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Resource' - description: Resource data - required: true - responses: - "201": - content: - application/json: - schema: - $ref: '#/components/schemas/Resource' - description: Created - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Validation errors occurred - "401": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Auth token is invalid - "403": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Unauthorized to perform operation - "409": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Resource already exists - "500": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: An unexpected error occurred creating the resource - security: - - Bearer: [] - summary: Create a new resource /api/maestro/v1/resources/{id}: delete: parameters: @@ -259,72 +211,10 @@ paths: description: Unexpected error occurred security: - Bearer: [] - summary: Get an resource by id - patch: - parameters: - - description: The id of record - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ResourcePatchRequest' - description: Updated resource data - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Resource' - description: Resource updated successfully - "400": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Validation errors occurred - "401": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Auth token is invalid - "403": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Unauthorized to perform operation - "404": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: No resource with specified id exists - "409": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Resource already exists - "500": - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: Unexpected error updating resource - security: - - Bearer: [] - summary: Update an resource + summary: Get a resource by id /api/maestro/v1/resource-bundles: get: + deprecated: true parameters: - description: Page number of record list when record list exceeds specified page size @@ -435,6 +325,7 @@ paths: summary: Returns a list of resource bundles /api/maestro/v1/resource-bundles/{id}: get: + deprecated: true parameters: - description: The id of record explode: false @@ -477,7 +368,7 @@ paths: description: Unexpected error occurred security: - Bearer: [] - summary: Get an resource bundle by id + summary: Get a resource bundle by id /api/maestro/v1/consumers: get: parameters: @@ -726,7 +617,7 @@ paths: description: Unexpected error occurred security: - Bearer: [] - summary: Get an consumer by id + summary: Get a consumer by id patch: parameters: - description: The id of record @@ -927,30 +818,14 @@ components: allOf: - $ref: '#/components/schemas/List' - $ref: '#/components/schemas/ResourceList_allOf' - ResourcePatchRequest: - example: - delete_option: "{}" - update_strategy: "{}" - manifest: "{}" - version: 0 - properties: - version: - type: integer - manifest: - type: object - delete_option: - type: object - update_strategy: - type: object - type: object + ResourceBundle: + allOf: + - $ref: '#/components/schemas/ObjectReference' + - $ref: '#/components/schemas/Resource_allOf' ResourceBundleList: allOf: - $ref: '#/components/schemas/List' - $ref: '#/components/schemas/ResourceBundleList_allOf' - ResourceBundle: - allOf: - - $ref: '#/components/schemas/ObjectReference' - - $ref: '#/components/schemas/ResourceBundle_allOf' Consumer: allOf: - $ref: '#/components/schemas/ObjectReference' @@ -1004,14 +879,18 @@ components: deleted_at: format: date-time type: string - manifest: - type: object - group_resource: + metadata: type: object + manifests: + items: + type: object + type: array delete_option: type: object - update_strategy: - type: object + manifest_configs: + items: + type: object + type: array status: type: object type: object @@ -1032,39 +911,6 @@ components: type: array type: object example: null - ResourceBundle_allOf: - properties: - name: - type: string - consumer_name: - type: string - version: - type: integer - created_at: - format: date-time - type: string - updated_at: - format: date-time - type: string - deleted_at: - format: date-time - type: string - metadata: - type: object - manifests: - items: - type: object - type: array - delete_option: - type: object - manifest_configs: - items: - type: object - type: array - status: - type: object - type: object - example: null Consumer_allOf: properties: name: diff --git a/pkg/api/openapi/api_default.go b/pkg/api/openapi/api_default.go index 96a74e55..eedea2e4 100644 --- a/pkg/api/openapi/api_default.go +++ b/pkg/api/openapi/api_default.go @@ -357,7 +357,7 @@ func (r ApiApiMaestroV1ConsumersIdGetRequest) Execute() (*Consumer, *http.Respon } /* -ApiMaestroV1ConsumersIdGet Get an consumer by id +ApiMaestroV1ConsumersIdGet Get a consumer by id @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param id The id of record @@ -883,6 +883,8 @@ ApiMaestroV1ResourceBundlesGet Returns a list of resource bundles @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @return ApiApiMaestroV1ResourceBundlesGetRequest + +Deprecated */ func (a *DefaultApiService) ApiMaestroV1ResourceBundlesGet(ctx context.Context) ApiApiMaestroV1ResourceBundlesGetRequest { return ApiApiMaestroV1ResourceBundlesGetRequest{ @@ -894,6 +896,8 @@ func (a *DefaultApiService) ApiMaestroV1ResourceBundlesGet(ctx context.Context) // Execute executes the request // // @return ResourceBundleList +// +// Deprecated func (a *DefaultApiService) ApiMaestroV1ResourceBundlesGetExecute(r ApiApiMaestroV1ResourceBundlesGetRequest) (*ResourceBundleList, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet @@ -1025,11 +1029,13 @@ func (r ApiApiMaestroV1ResourceBundlesIdGetRequest) Execute() (*ResourceBundle, } /* -ApiMaestroV1ResourceBundlesIdGet Get an resource bundle by id +ApiMaestroV1ResourceBundlesIdGet Get a resource bundle by id @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param id The id of record @return ApiApiMaestroV1ResourceBundlesIdGetRequest + +Deprecated */ func (a *DefaultApiService) ApiMaestroV1ResourceBundlesIdGet(ctx context.Context, id string) ApiApiMaestroV1ResourceBundlesIdGetRequest { return ApiApiMaestroV1ResourceBundlesIdGetRequest{ @@ -1042,6 +1048,8 @@ func (a *DefaultApiService) ApiMaestroV1ResourceBundlesIdGet(ctx context.Context // Execute executes the request // // @return ResourceBundle +// +// Deprecated func (a *DefaultApiService) ApiMaestroV1ResourceBundlesIdGetExecute(r ApiApiMaestroV1ResourceBundlesIdGetRequest) (*ResourceBundle, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet @@ -1494,7 +1502,7 @@ func (r ApiApiMaestroV1ResourcesIdGetRequest) Execute() (*Resource, *http.Respon } /* -ApiMaestroV1ResourcesIdGet Get an resource by id +ApiMaestroV1ResourcesIdGet Get a resource by id @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param id The id of record @@ -1627,346 +1635,3 @@ func (a *DefaultApiService) ApiMaestroV1ResourcesIdGetExecute(r ApiApiMaestroV1R return localVarReturnValue, localVarHTTPResponse, nil } - -type ApiApiMaestroV1ResourcesIdPatchRequest struct { - ctx context.Context - ApiService *DefaultApiService - id string - resourcePatchRequest *ResourcePatchRequest -} - -// Updated resource data -func (r ApiApiMaestroV1ResourcesIdPatchRequest) ResourcePatchRequest(resourcePatchRequest ResourcePatchRequest) ApiApiMaestroV1ResourcesIdPatchRequest { - r.resourcePatchRequest = &resourcePatchRequest - return r -} - -func (r ApiApiMaestroV1ResourcesIdPatchRequest) Execute() (*Resource, *http.Response, error) { - return r.ApiService.ApiMaestroV1ResourcesIdPatchExecute(r) -} - -/* -ApiMaestroV1ResourcesIdPatch Update an resource - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id The id of record - @return ApiApiMaestroV1ResourcesIdPatchRequest -*/ -func (a *DefaultApiService) ApiMaestroV1ResourcesIdPatch(ctx context.Context, id string) ApiApiMaestroV1ResourcesIdPatchRequest { - return ApiApiMaestroV1ResourcesIdPatchRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// -// @return Resource -func (a *DefaultApiService) ApiMaestroV1ResourcesIdPatchExecute(r ApiApiMaestroV1ResourcesIdPatchRequest) (*Resource, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPatch - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Resource - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DefaultApiService.ApiMaestroV1ResourcesIdPatch") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/api/maestro/v1/resources/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.resourcePatchRequest == nil { - return localVarReturnValue, nil, reportError("resourcePatchRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - // body params - localVarPostBody = r.resourcePatchRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 400 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 403 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 404 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ApiApiMaestroV1ResourcesPostRequest struct { - ctx context.Context - ApiService *DefaultApiService - resource *Resource -} - -// Resource data -func (r ApiApiMaestroV1ResourcesPostRequest) Resource(resource Resource) ApiApiMaestroV1ResourcesPostRequest { - r.resource = &resource - return r -} - -func (r ApiApiMaestroV1ResourcesPostRequest) Execute() (*Resource, *http.Response, error) { - return r.ApiService.ApiMaestroV1ResourcesPostExecute(r) -} - -/* -ApiMaestroV1ResourcesPost Create a new resource - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return ApiApiMaestroV1ResourcesPostRequest -*/ -func (a *DefaultApiService) ApiMaestroV1ResourcesPost(ctx context.Context) ApiApiMaestroV1ResourcesPostRequest { - return ApiApiMaestroV1ResourcesPostRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// -// @return Resource -func (a *DefaultApiService) ApiMaestroV1ResourcesPostExecute(r ApiApiMaestroV1ResourcesPostRequest) (*Resource, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Resource - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DefaultApiService.ApiMaestroV1ResourcesPost") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/api/maestro/v1/resources" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.resource == nil { - return localVarReturnValue, nil, reportError("resource is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - // body params - localVarPostBody = r.resource - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 400 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 403 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 409 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} diff --git a/pkg/api/openapi/docs/DefaultApi.md b/pkg/api/openapi/docs/DefaultApi.md index 093e9108..e5f00372 100644 --- a/pkg/api/openapi/docs/DefaultApi.md +++ b/pkg/api/openapi/docs/DefaultApi.md @@ -6,16 +6,14 @@ Method | HTTP request | Description ------------- | ------------- | ------------- [**ApiMaestroV1ConsumersGet**](DefaultApi.md#ApiMaestroV1ConsumersGet) | **Get** /api/maestro/v1/consumers | Returns a list of consumers [**ApiMaestroV1ConsumersIdDelete**](DefaultApi.md#ApiMaestroV1ConsumersIdDelete) | **Delete** /api/maestro/v1/consumers/{id} | Delete a consumer -[**ApiMaestroV1ConsumersIdGet**](DefaultApi.md#ApiMaestroV1ConsumersIdGet) | **Get** /api/maestro/v1/consumers/{id} | Get an consumer by id +[**ApiMaestroV1ConsumersIdGet**](DefaultApi.md#ApiMaestroV1ConsumersIdGet) | **Get** /api/maestro/v1/consumers/{id} | Get a consumer by id [**ApiMaestroV1ConsumersIdPatch**](DefaultApi.md#ApiMaestroV1ConsumersIdPatch) | **Patch** /api/maestro/v1/consumers/{id} | Update an consumer [**ApiMaestroV1ConsumersPost**](DefaultApi.md#ApiMaestroV1ConsumersPost) | **Post** /api/maestro/v1/consumers | Create a new consumer [**ApiMaestroV1ResourceBundlesGet**](DefaultApi.md#ApiMaestroV1ResourceBundlesGet) | **Get** /api/maestro/v1/resource-bundles | Returns a list of resource bundles -[**ApiMaestroV1ResourceBundlesIdGet**](DefaultApi.md#ApiMaestroV1ResourceBundlesIdGet) | **Get** /api/maestro/v1/resource-bundles/{id} | Get an resource bundle by id +[**ApiMaestroV1ResourceBundlesIdGet**](DefaultApi.md#ApiMaestroV1ResourceBundlesIdGet) | **Get** /api/maestro/v1/resource-bundles/{id} | Get a resource bundle by id [**ApiMaestroV1ResourcesGet**](DefaultApi.md#ApiMaestroV1ResourcesGet) | **Get** /api/maestro/v1/resources | Returns a list of resources [**ApiMaestroV1ResourcesIdDelete**](DefaultApi.md#ApiMaestroV1ResourcesIdDelete) | **Delete** /api/maestro/v1/resources/{id} | Delete a resource -[**ApiMaestroV1ResourcesIdGet**](DefaultApi.md#ApiMaestroV1ResourcesIdGet) | **Get** /api/maestro/v1/resources/{id} | Get an resource by id -[**ApiMaestroV1ResourcesIdPatch**](DefaultApi.md#ApiMaestroV1ResourcesIdPatch) | **Patch** /api/maestro/v1/resources/{id} | Update an resource -[**ApiMaestroV1ResourcesPost**](DefaultApi.md#ApiMaestroV1ResourcesPost) | **Post** /api/maestro/v1/resources | Create a new resource +[**ApiMaestroV1ResourcesIdGet**](DefaultApi.md#ApiMaestroV1ResourcesIdGet) | **Get** /api/maestro/v1/resources/{id} | Get a resource by id @@ -161,7 +159,7 @@ Name | Type | Description | Notes > Consumer ApiMaestroV1ConsumersIdGet(ctx, id).Execute() -Get an consumer by id +Get a consumer by id ### Example @@ -435,7 +433,7 @@ Name | Type | Description | Notes > ResourceBundle ApiMaestroV1ResourceBundlesIdGet(ctx, id).Execute() -Get an resource bundle by id +Get a resource bundle by id ### Example @@ -641,7 +639,7 @@ Name | Type | Description | Notes > Resource ApiMaestroV1ResourcesIdGet(ctx, id).Execute() -Get an resource by id +Get a resource by id ### Example @@ -704,137 +702,3 @@ Name | Type | Description | Notes [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) - -## ApiMaestroV1ResourcesIdPatch - -> Resource ApiMaestroV1ResourcesIdPatch(ctx, id).ResourcePatchRequest(resourcePatchRequest).Execute() - -Update an resource - -### Example - -```go -package main - -import ( - "context" - "fmt" - "os" - openapiclient "github.com/GIT_USER_ID/GIT_REPO_ID" -) - -func main() { - id := "id_example" // string | The id of record - resourcePatchRequest := *openapiclient.NewResourcePatchRequest() // ResourcePatchRequest | Updated resource data - - configuration := openapiclient.NewConfiguration() - apiClient := openapiclient.NewAPIClient(configuration) - resp, r, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdPatch(context.Background(), id).ResourcePatchRequest(resourcePatchRequest).Execute() - if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `DefaultApi.ApiMaestroV1ResourcesIdPatch``: %v\n", err) - fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) - } - // response from `ApiMaestroV1ResourcesIdPatch`: Resource - fmt.Fprintf(os.Stdout, "Response from `DefaultApi.ApiMaestroV1ResourcesIdPatch`: %v\n", resp) -} -``` - -### Path Parameters - - -Name | Type | Description | Notes -------------- | ------------- | ------------- | ------------- -**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. -**id** | **string** | The id of record | - -### Other Parameters - -Other parameters are passed through a pointer to a apiApiMaestroV1ResourcesIdPatchRequest struct via the builder pattern - - -Name | Type | Description | Notes -------------- | ------------- | ------------- | ------------- - - **resourcePatchRequest** | [**ResourcePatchRequest**](ResourcePatchRequest.md) | Updated resource data | - -### Return type - -[**Resource**](Resource.md) - -### Authorization - -[Bearer](../README.md#Bearer) - -### HTTP request headers - -- **Content-Type**: application/json -- **Accept**: application/json - -[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) -[[Back to Model list]](../README.md#documentation-for-models) -[[Back to README]](../README.md) - - -## ApiMaestroV1ResourcesPost - -> Resource ApiMaestroV1ResourcesPost(ctx).Resource(resource).Execute() - -Create a new resource - -### Example - -```go -package main - -import ( - "context" - "fmt" - "os" - openapiclient "github.com/GIT_USER_ID/GIT_REPO_ID" -) - -func main() { - resource := *openapiclient.NewResource() // Resource | Resource data - - configuration := openapiclient.NewConfiguration() - apiClient := openapiclient.NewAPIClient(configuration) - resp, r, err := apiClient.DefaultApi.ApiMaestroV1ResourcesPost(context.Background()).Resource(resource).Execute() - if err != nil { - fmt.Fprintf(os.Stderr, "Error when calling `DefaultApi.ApiMaestroV1ResourcesPost``: %v\n", err) - fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) - } - // response from `ApiMaestroV1ResourcesPost`: Resource - fmt.Fprintf(os.Stdout, "Response from `DefaultApi.ApiMaestroV1ResourcesPost`: %v\n", resp) -} -``` - -### Path Parameters - - - -### Other Parameters - -Other parameters are passed through a pointer to a apiApiMaestroV1ResourcesPostRequest struct via the builder pattern - - -Name | Type | Description | Notes -------------- | ------------- | ------------- | ------------- - **resource** | [**Resource**](Resource.md) | Resource data | - -### Return type - -[**Resource**](Resource.md) - -### Authorization - -[Bearer](../README.md#Bearer) - -### HTTP request headers - -- **Content-Type**: application/json -- **Accept**: application/json - -[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) -[[Back to Model list]](../README.md#documentation-for-models) -[[Back to README]](../README.md) - diff --git a/pkg/api/openapi/docs/Resource.md b/pkg/api/openapi/docs/Resource.md index 5af52e9a..d097ab64 100644 --- a/pkg/api/openapi/docs/Resource.md +++ b/pkg/api/openapi/docs/Resource.md @@ -13,10 +13,10 @@ Name | Type | Description | Notes **CreatedAt** | Pointer to **time.Time** | | [optional] **UpdatedAt** | Pointer to **time.Time** | | [optional] **DeletedAt** | Pointer to **time.Time** | | [optional] -**Manifest** | Pointer to **map[string]interface{}** | | [optional] -**GroupResource** | Pointer to **map[string]interface{}** | | [optional] +**Metadata** | Pointer to **map[string]interface{}** | | [optional] +**Manifests** | Pointer to **[]map[string]interface{}** | | [optional] **DeleteOption** | Pointer to **map[string]interface{}** | | [optional] -**UpdateStrategy** | Pointer to **map[string]interface{}** | | [optional] +**ManifestConfigs** | Pointer to **[]map[string]interface{}** | | [optional] **Status** | Pointer to **map[string]interface{}** | | [optional] ## Methods @@ -263,55 +263,55 @@ SetDeletedAt sets DeletedAt field to given value. HasDeletedAt returns a boolean if a field has been set. -### GetManifest +### GetMetadata -`func (o *Resource) GetManifest() map[string]interface{}` +`func (o *Resource) GetMetadata() map[string]interface{}` -GetManifest returns the Manifest field if non-nil, zero value otherwise. +GetMetadata returns the Metadata field if non-nil, zero value otherwise. -### GetManifestOk +### GetMetadataOk -`func (o *Resource) GetManifestOk() (*map[string]interface{}, bool)` +`func (o *Resource) GetMetadataOk() (*map[string]interface{}, bool)` -GetManifestOk returns a tuple with the Manifest field if it's non-nil, zero value otherwise +GetMetadataOk returns a tuple with the Metadata field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetManifest +### SetMetadata -`func (o *Resource) SetManifest(v map[string]interface{})` +`func (o *Resource) SetMetadata(v map[string]interface{})` -SetManifest sets Manifest field to given value. +SetMetadata sets Metadata field to given value. -### HasManifest +### HasMetadata -`func (o *Resource) HasManifest() bool` +`func (o *Resource) HasMetadata() bool` -HasManifest returns a boolean if a field has been set. +HasMetadata returns a boolean if a field has been set. -### GetGroupResource +### GetManifests -`func (o *Resource) GetGroupResource() map[string]interface{}` +`func (o *Resource) GetManifests() []map[string]interface{}` -GetGroupResource returns the GroupResource field if non-nil, zero value otherwise. +GetManifests returns the Manifests field if non-nil, zero value otherwise. -### GetGroupResourceOk +### GetManifestsOk -`func (o *Resource) GetGroupResourceOk() (*map[string]interface{}, bool)` +`func (o *Resource) GetManifestsOk() (*[]map[string]interface{}, bool)` -GetGroupResourceOk returns a tuple with the GroupResource field if it's non-nil, zero value otherwise +GetManifestsOk returns a tuple with the Manifests field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetGroupResource +### SetManifests -`func (o *Resource) SetGroupResource(v map[string]interface{})` +`func (o *Resource) SetManifests(v []map[string]interface{})` -SetGroupResource sets GroupResource field to given value. +SetManifests sets Manifests field to given value. -### HasGroupResource +### HasManifests -`func (o *Resource) HasGroupResource() bool` +`func (o *Resource) HasManifests() bool` -HasGroupResource returns a boolean if a field has been set. +HasManifests returns a boolean if a field has been set. ### GetDeleteOption @@ -338,30 +338,30 @@ SetDeleteOption sets DeleteOption field to given value. HasDeleteOption returns a boolean if a field has been set. -### GetUpdateStrategy +### GetManifestConfigs -`func (o *Resource) GetUpdateStrategy() map[string]interface{}` +`func (o *Resource) GetManifestConfigs() []map[string]interface{}` -GetUpdateStrategy returns the UpdateStrategy field if non-nil, zero value otherwise. +GetManifestConfigs returns the ManifestConfigs field if non-nil, zero value otherwise. -### GetUpdateStrategyOk +### GetManifestConfigsOk -`func (o *Resource) GetUpdateStrategyOk() (*map[string]interface{}, bool)` +`func (o *Resource) GetManifestConfigsOk() (*[]map[string]interface{}, bool)` -GetUpdateStrategyOk returns a tuple with the UpdateStrategy field if it's non-nil, zero value otherwise +GetManifestConfigsOk returns a tuple with the ManifestConfigs field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetUpdateStrategy +### SetManifestConfigs -`func (o *Resource) SetUpdateStrategy(v map[string]interface{})` +`func (o *Resource) SetManifestConfigs(v []map[string]interface{})` -SetUpdateStrategy sets UpdateStrategy field to given value. +SetManifestConfigs sets ManifestConfigs field to given value. -### HasUpdateStrategy +### HasManifestConfigs -`func (o *Resource) HasUpdateStrategy() bool` +`func (o *Resource) HasManifestConfigs() bool` -HasUpdateStrategy returns a boolean if a field has been set. +HasManifestConfigs returns a boolean if a field has been set. ### GetStatus diff --git a/pkg/api/openapi/docs/ResourceAllOf.md b/pkg/api/openapi/docs/ResourceAllOf.md index 028f47ee..150647f4 100644 --- a/pkg/api/openapi/docs/ResourceAllOf.md +++ b/pkg/api/openapi/docs/ResourceAllOf.md @@ -10,10 +10,10 @@ Name | Type | Description | Notes **CreatedAt** | Pointer to **time.Time** | | [optional] **UpdatedAt** | Pointer to **time.Time** | | [optional] **DeletedAt** | Pointer to **time.Time** | | [optional] -**Manifest** | Pointer to **map[string]interface{}** | | [optional] -**GroupResource** | Pointer to **map[string]interface{}** | | [optional] +**Metadata** | Pointer to **map[string]interface{}** | | [optional] +**Manifests** | Pointer to **[]map[string]interface{}** | | [optional] **DeleteOption** | Pointer to **map[string]interface{}** | | [optional] -**UpdateStrategy** | Pointer to **map[string]interface{}** | | [optional] +**ManifestConfigs** | Pointer to **[]map[string]interface{}** | | [optional] **Status** | Pointer to **map[string]interface{}** | | [optional] ## Methods @@ -185,55 +185,55 @@ SetDeletedAt sets DeletedAt field to given value. HasDeletedAt returns a boolean if a field has been set. -### GetManifest +### GetMetadata -`func (o *ResourceAllOf) GetManifest() map[string]interface{}` +`func (o *ResourceAllOf) GetMetadata() map[string]interface{}` -GetManifest returns the Manifest field if non-nil, zero value otherwise. +GetMetadata returns the Metadata field if non-nil, zero value otherwise. -### GetManifestOk +### GetMetadataOk -`func (o *ResourceAllOf) GetManifestOk() (*map[string]interface{}, bool)` +`func (o *ResourceAllOf) GetMetadataOk() (*map[string]interface{}, bool)` -GetManifestOk returns a tuple with the Manifest field if it's non-nil, zero value otherwise +GetMetadataOk returns a tuple with the Metadata field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetManifest +### SetMetadata -`func (o *ResourceAllOf) SetManifest(v map[string]interface{})` +`func (o *ResourceAllOf) SetMetadata(v map[string]interface{})` -SetManifest sets Manifest field to given value. +SetMetadata sets Metadata field to given value. -### HasManifest +### HasMetadata -`func (o *ResourceAllOf) HasManifest() bool` +`func (o *ResourceAllOf) HasMetadata() bool` -HasManifest returns a boolean if a field has been set. +HasMetadata returns a boolean if a field has been set. -### GetGroupResource +### GetManifests -`func (o *ResourceAllOf) GetGroupResource() map[string]interface{}` +`func (o *ResourceAllOf) GetManifests() []map[string]interface{}` -GetGroupResource returns the GroupResource field if non-nil, zero value otherwise. +GetManifests returns the Manifests field if non-nil, zero value otherwise. -### GetGroupResourceOk +### GetManifestsOk -`func (o *ResourceAllOf) GetGroupResourceOk() (*map[string]interface{}, bool)` +`func (o *ResourceAllOf) GetManifestsOk() (*[]map[string]interface{}, bool)` -GetGroupResourceOk returns a tuple with the GroupResource field if it's non-nil, zero value otherwise +GetManifestsOk returns a tuple with the Manifests field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetGroupResource +### SetManifests -`func (o *ResourceAllOf) SetGroupResource(v map[string]interface{})` +`func (o *ResourceAllOf) SetManifests(v []map[string]interface{})` -SetGroupResource sets GroupResource field to given value. +SetManifests sets Manifests field to given value. -### HasGroupResource +### HasManifests -`func (o *ResourceAllOf) HasGroupResource() bool` +`func (o *ResourceAllOf) HasManifests() bool` -HasGroupResource returns a boolean if a field has been set. +HasManifests returns a boolean if a field has been set. ### GetDeleteOption @@ -260,30 +260,30 @@ SetDeleteOption sets DeleteOption field to given value. HasDeleteOption returns a boolean if a field has been set. -### GetUpdateStrategy +### GetManifestConfigs -`func (o *ResourceAllOf) GetUpdateStrategy() map[string]interface{}` +`func (o *ResourceAllOf) GetManifestConfigs() []map[string]interface{}` -GetUpdateStrategy returns the UpdateStrategy field if non-nil, zero value otherwise. +GetManifestConfigs returns the ManifestConfigs field if non-nil, zero value otherwise. -### GetUpdateStrategyOk +### GetManifestConfigsOk -`func (o *ResourceAllOf) GetUpdateStrategyOk() (*map[string]interface{}, bool)` +`func (o *ResourceAllOf) GetManifestConfigsOk() (*[]map[string]interface{}, bool)` -GetUpdateStrategyOk returns a tuple with the UpdateStrategy field if it's non-nil, zero value otherwise +GetManifestConfigsOk returns a tuple with the ManifestConfigs field if it's non-nil, zero value otherwise and a boolean to check if the value has been set. -### SetUpdateStrategy +### SetManifestConfigs -`func (o *ResourceAllOf) SetUpdateStrategy(v map[string]interface{})` +`func (o *ResourceAllOf) SetManifestConfigs(v []map[string]interface{})` -SetUpdateStrategy sets UpdateStrategy field to given value. +SetManifestConfigs sets ManifestConfigs field to given value. -### HasUpdateStrategy +### HasManifestConfigs -`func (o *ResourceAllOf) HasUpdateStrategy() bool` +`func (o *ResourceAllOf) HasManifestConfigs() bool` -HasUpdateStrategy returns a boolean if a field has been set. +HasManifestConfigs returns a boolean if a field has been set. ### GetStatus diff --git a/pkg/api/openapi/docs/ResourceBundleAllOf.md b/pkg/api/openapi/docs/ResourceBundleAllOf.md deleted file mode 100644 index cde4a5b8..00000000 --- a/pkg/api/openapi/docs/ResourceBundleAllOf.md +++ /dev/null @@ -1,316 +0,0 @@ -# ResourceBundleAllOf - -## Properties - -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- -**Name** | Pointer to **string** | | [optional] -**ConsumerName** | Pointer to **string** | | [optional] -**Version** | Pointer to **int32** | | [optional] -**CreatedAt** | Pointer to **time.Time** | | [optional] -**UpdatedAt** | Pointer to **time.Time** | | [optional] -**DeletedAt** | Pointer to **time.Time** | | [optional] -**Metadata** | Pointer to **map[string]interface{}** | | [optional] -**Manifests** | Pointer to **[]map[string]interface{}** | | [optional] -**DeleteOption** | Pointer to **map[string]interface{}** | | [optional] -**ManifestConfigs** | Pointer to **[]map[string]interface{}** | | [optional] -**Status** | Pointer to **map[string]interface{}** | | [optional] - -## Methods - -### NewResourceBundleAllOf - -`func NewResourceBundleAllOf() *ResourceBundleAllOf` - -NewResourceBundleAllOf instantiates a new ResourceBundleAllOf object -This constructor will assign default values to properties that have it defined, -and makes sure properties required by API are set, but the set of arguments -will change when the set of required properties is changed - -### NewResourceBundleAllOfWithDefaults - -`func NewResourceBundleAllOfWithDefaults() *ResourceBundleAllOf` - -NewResourceBundleAllOfWithDefaults instantiates a new ResourceBundleAllOf object -This constructor will only assign default values to properties that have it defined, -but it doesn't guarantee that properties required by API are set - -### GetName - -`func (o *ResourceBundleAllOf) GetName() string` - -GetName returns the Name field if non-nil, zero value otherwise. - -### GetNameOk - -`func (o *ResourceBundleAllOf) GetNameOk() (*string, bool)` - -GetNameOk returns a tuple with the Name field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetName - -`func (o *ResourceBundleAllOf) SetName(v string)` - -SetName sets Name field to given value. - -### HasName - -`func (o *ResourceBundleAllOf) HasName() bool` - -HasName returns a boolean if a field has been set. - -### GetConsumerName - -`func (o *ResourceBundleAllOf) GetConsumerName() string` - -GetConsumerName returns the ConsumerName field if non-nil, zero value otherwise. - -### GetConsumerNameOk - -`func (o *ResourceBundleAllOf) GetConsumerNameOk() (*string, bool)` - -GetConsumerNameOk returns a tuple with the ConsumerName field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetConsumerName - -`func (o *ResourceBundleAllOf) SetConsumerName(v string)` - -SetConsumerName sets ConsumerName field to given value. - -### HasConsumerName - -`func (o *ResourceBundleAllOf) HasConsumerName() bool` - -HasConsumerName returns a boolean if a field has been set. - -### GetVersion - -`func (o *ResourceBundleAllOf) GetVersion() int32` - -GetVersion returns the Version field if non-nil, zero value otherwise. - -### GetVersionOk - -`func (o *ResourceBundleAllOf) GetVersionOk() (*int32, bool)` - -GetVersionOk returns a tuple with the Version field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetVersion - -`func (o *ResourceBundleAllOf) SetVersion(v int32)` - -SetVersion sets Version field to given value. - -### HasVersion - -`func (o *ResourceBundleAllOf) HasVersion() bool` - -HasVersion returns a boolean if a field has been set. - -### GetCreatedAt - -`func (o *ResourceBundleAllOf) GetCreatedAt() time.Time` - -GetCreatedAt returns the CreatedAt field if non-nil, zero value otherwise. - -### GetCreatedAtOk - -`func (o *ResourceBundleAllOf) GetCreatedAtOk() (*time.Time, bool)` - -GetCreatedAtOk returns a tuple with the CreatedAt field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetCreatedAt - -`func (o *ResourceBundleAllOf) SetCreatedAt(v time.Time)` - -SetCreatedAt sets CreatedAt field to given value. - -### HasCreatedAt - -`func (o *ResourceBundleAllOf) HasCreatedAt() bool` - -HasCreatedAt returns a boolean if a field has been set. - -### GetUpdatedAt - -`func (o *ResourceBundleAllOf) GetUpdatedAt() time.Time` - -GetUpdatedAt returns the UpdatedAt field if non-nil, zero value otherwise. - -### GetUpdatedAtOk - -`func (o *ResourceBundleAllOf) GetUpdatedAtOk() (*time.Time, bool)` - -GetUpdatedAtOk returns a tuple with the UpdatedAt field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetUpdatedAt - -`func (o *ResourceBundleAllOf) SetUpdatedAt(v time.Time)` - -SetUpdatedAt sets UpdatedAt field to given value. - -### HasUpdatedAt - -`func (o *ResourceBundleAllOf) HasUpdatedAt() bool` - -HasUpdatedAt returns a boolean if a field has been set. - -### GetDeletedAt - -`func (o *ResourceBundleAllOf) GetDeletedAt() time.Time` - -GetDeletedAt returns the DeletedAt field if non-nil, zero value otherwise. - -### GetDeletedAtOk - -`func (o *ResourceBundleAllOf) GetDeletedAtOk() (*time.Time, bool)` - -GetDeletedAtOk returns a tuple with the DeletedAt field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetDeletedAt - -`func (o *ResourceBundleAllOf) SetDeletedAt(v time.Time)` - -SetDeletedAt sets DeletedAt field to given value. - -### HasDeletedAt - -`func (o *ResourceBundleAllOf) HasDeletedAt() bool` - -HasDeletedAt returns a boolean if a field has been set. - -### GetMetadata - -`func (o *ResourceBundleAllOf) GetMetadata() map[string]interface{}` - -GetMetadata returns the Metadata field if non-nil, zero value otherwise. - -### GetMetadataOk - -`func (o *ResourceBundleAllOf) GetMetadataOk() (*map[string]interface{}, bool)` - -GetMetadataOk returns a tuple with the Metadata field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetMetadata - -`func (o *ResourceBundleAllOf) SetMetadata(v map[string]interface{})` - -SetMetadata sets Metadata field to given value. - -### HasMetadata - -`func (o *ResourceBundleAllOf) HasMetadata() bool` - -HasMetadata returns a boolean if a field has been set. - -### GetManifests - -`func (o *ResourceBundleAllOf) GetManifests() []map[string]interface{}` - -GetManifests returns the Manifests field if non-nil, zero value otherwise. - -### GetManifestsOk - -`func (o *ResourceBundleAllOf) GetManifestsOk() (*[]map[string]interface{}, bool)` - -GetManifestsOk returns a tuple with the Manifests field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetManifests - -`func (o *ResourceBundleAllOf) SetManifests(v []map[string]interface{})` - -SetManifests sets Manifests field to given value. - -### HasManifests - -`func (o *ResourceBundleAllOf) HasManifests() bool` - -HasManifests returns a boolean if a field has been set. - -### GetDeleteOption - -`func (o *ResourceBundleAllOf) GetDeleteOption() map[string]interface{}` - -GetDeleteOption returns the DeleteOption field if non-nil, zero value otherwise. - -### GetDeleteOptionOk - -`func (o *ResourceBundleAllOf) GetDeleteOptionOk() (*map[string]interface{}, bool)` - -GetDeleteOptionOk returns a tuple with the DeleteOption field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetDeleteOption - -`func (o *ResourceBundleAllOf) SetDeleteOption(v map[string]interface{})` - -SetDeleteOption sets DeleteOption field to given value. - -### HasDeleteOption - -`func (o *ResourceBundleAllOf) HasDeleteOption() bool` - -HasDeleteOption returns a boolean if a field has been set. - -### GetManifestConfigs - -`func (o *ResourceBundleAllOf) GetManifestConfigs() []map[string]interface{}` - -GetManifestConfigs returns the ManifestConfigs field if non-nil, zero value otherwise. - -### GetManifestConfigsOk - -`func (o *ResourceBundleAllOf) GetManifestConfigsOk() (*[]map[string]interface{}, bool)` - -GetManifestConfigsOk returns a tuple with the ManifestConfigs field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetManifestConfigs - -`func (o *ResourceBundleAllOf) SetManifestConfigs(v []map[string]interface{})` - -SetManifestConfigs sets ManifestConfigs field to given value. - -### HasManifestConfigs - -`func (o *ResourceBundleAllOf) HasManifestConfigs() bool` - -HasManifestConfigs returns a boolean if a field has been set. - -### GetStatus - -`func (o *ResourceBundleAllOf) GetStatus() map[string]interface{}` - -GetStatus returns the Status field if non-nil, zero value otherwise. - -### GetStatusOk - -`func (o *ResourceBundleAllOf) GetStatusOk() (*map[string]interface{}, bool)` - -GetStatusOk returns a tuple with the Status field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetStatus - -`func (o *ResourceBundleAllOf) SetStatus(v map[string]interface{})` - -SetStatus sets Status field to given value. - -### HasStatus - -`func (o *ResourceBundleAllOf) HasStatus() bool` - -HasStatus returns a boolean if a field has been set. - - -[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) - - diff --git a/pkg/api/openapi/docs/ResourcePatchRequest.md b/pkg/api/openapi/docs/ResourcePatchRequest.md deleted file mode 100644 index eebeba0d..00000000 --- a/pkg/api/openapi/docs/ResourcePatchRequest.md +++ /dev/null @@ -1,134 +0,0 @@ -# ResourcePatchRequest - -## Properties - -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- -**Version** | Pointer to **int32** | | [optional] -**Manifest** | Pointer to **map[string]interface{}** | | [optional] -**DeleteOption** | Pointer to **map[string]interface{}** | | [optional] -**UpdateStrategy** | Pointer to **map[string]interface{}** | | [optional] - -## Methods - -### NewResourcePatchRequest - -`func NewResourcePatchRequest() *ResourcePatchRequest` - -NewResourcePatchRequest instantiates a new ResourcePatchRequest object -This constructor will assign default values to properties that have it defined, -and makes sure properties required by API are set, but the set of arguments -will change when the set of required properties is changed - -### NewResourcePatchRequestWithDefaults - -`func NewResourcePatchRequestWithDefaults() *ResourcePatchRequest` - -NewResourcePatchRequestWithDefaults instantiates a new ResourcePatchRequest object -This constructor will only assign default values to properties that have it defined, -but it doesn't guarantee that properties required by API are set - -### GetVersion - -`func (o *ResourcePatchRequest) GetVersion() int32` - -GetVersion returns the Version field if non-nil, zero value otherwise. - -### GetVersionOk - -`func (o *ResourcePatchRequest) GetVersionOk() (*int32, bool)` - -GetVersionOk returns a tuple with the Version field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetVersion - -`func (o *ResourcePatchRequest) SetVersion(v int32)` - -SetVersion sets Version field to given value. - -### HasVersion - -`func (o *ResourcePatchRequest) HasVersion() bool` - -HasVersion returns a boolean if a field has been set. - -### GetManifest - -`func (o *ResourcePatchRequest) GetManifest() map[string]interface{}` - -GetManifest returns the Manifest field if non-nil, zero value otherwise. - -### GetManifestOk - -`func (o *ResourcePatchRequest) GetManifestOk() (*map[string]interface{}, bool)` - -GetManifestOk returns a tuple with the Manifest field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetManifest - -`func (o *ResourcePatchRequest) SetManifest(v map[string]interface{})` - -SetManifest sets Manifest field to given value. - -### HasManifest - -`func (o *ResourcePatchRequest) HasManifest() bool` - -HasManifest returns a boolean if a field has been set. - -### GetDeleteOption - -`func (o *ResourcePatchRequest) GetDeleteOption() map[string]interface{}` - -GetDeleteOption returns the DeleteOption field if non-nil, zero value otherwise. - -### GetDeleteOptionOk - -`func (o *ResourcePatchRequest) GetDeleteOptionOk() (*map[string]interface{}, bool)` - -GetDeleteOptionOk returns a tuple with the DeleteOption field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetDeleteOption - -`func (o *ResourcePatchRequest) SetDeleteOption(v map[string]interface{})` - -SetDeleteOption sets DeleteOption field to given value. - -### HasDeleteOption - -`func (o *ResourcePatchRequest) HasDeleteOption() bool` - -HasDeleteOption returns a boolean if a field has been set. - -### GetUpdateStrategy - -`func (o *ResourcePatchRequest) GetUpdateStrategy() map[string]interface{}` - -GetUpdateStrategy returns the UpdateStrategy field if non-nil, zero value otherwise. - -### GetUpdateStrategyOk - -`func (o *ResourcePatchRequest) GetUpdateStrategyOk() (*map[string]interface{}, bool)` - -GetUpdateStrategyOk returns a tuple with the UpdateStrategy field if it's non-nil, zero value otherwise -and a boolean to check if the value has been set. - -### SetUpdateStrategy - -`func (o *ResourcePatchRequest) SetUpdateStrategy(v map[string]interface{})` - -SetUpdateStrategy sets UpdateStrategy field to given value. - -### HasUpdateStrategy - -`func (o *ResourcePatchRequest) HasUpdateStrategy() bool` - -HasUpdateStrategy returns a boolean if a field has been set. - - -[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) - - diff --git a/pkg/api/openapi/model_resource.go b/pkg/api/openapi/model_resource.go index 03fb10bc..7b06932d 100644 --- a/pkg/api/openapi/model_resource.go +++ b/pkg/api/openapi/model_resource.go @@ -20,20 +20,20 @@ var _ MappedNullable = &Resource{} // Resource struct for Resource type Resource struct { - Id *string `json:"id,omitempty"` - Kind *string `json:"kind,omitempty"` - Href *string `json:"href,omitempty"` - Name *string `json:"name,omitempty"` - ConsumerName *string `json:"consumer_name,omitempty"` - Version *int32 `json:"version,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - DeletedAt *time.Time `json:"deleted_at,omitempty"` - Manifest map[string]interface{} `json:"manifest,omitempty"` - GroupResource map[string]interface{} `json:"group_resource,omitempty"` - DeleteOption map[string]interface{} `json:"delete_option,omitempty"` - UpdateStrategy map[string]interface{} `json:"update_strategy,omitempty"` - Status map[string]interface{} `json:"status,omitempty"` + Id *string `json:"id,omitempty"` + Kind *string `json:"kind,omitempty"` + Href *string `json:"href,omitempty"` + Name *string `json:"name,omitempty"` + ConsumerName *string `json:"consumer_name,omitempty"` + Version *int32 `json:"version,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + DeletedAt *time.Time `json:"deleted_at,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + Manifests []map[string]interface{} `json:"manifests,omitempty"` + DeleteOption map[string]interface{} `json:"delete_option,omitempty"` + ManifestConfigs []map[string]interface{} `json:"manifest_configs,omitempty"` + Status map[string]interface{} `json:"status,omitempty"` } // NewResource instantiates a new Resource object @@ -341,68 +341,68 @@ func (o *Resource) SetDeletedAt(v time.Time) { o.DeletedAt = &v } -// GetManifest returns the Manifest field value if set, zero value otherwise. -func (o *Resource) GetManifest() map[string]interface{} { - if o == nil || IsNil(o.Manifest) { +// GetMetadata returns the Metadata field value if set, zero value otherwise. +func (o *Resource) GetMetadata() map[string]interface{} { + if o == nil || IsNil(o.Metadata) { var ret map[string]interface{} return ret } - return o.Manifest + return o.Metadata } -// GetManifestOk returns a tuple with the Manifest field value if set, nil otherwise +// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *Resource) GetManifestOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Manifest) { +func (o *Resource) GetMetadataOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Metadata) { return map[string]interface{}{}, false } - return o.Manifest, true + return o.Metadata, true } -// HasManifest returns a boolean if a field has been set. -func (o *Resource) HasManifest() bool { - if o != nil && !IsNil(o.Manifest) { +// HasMetadata returns a boolean if a field has been set. +func (o *Resource) HasMetadata() bool { + if o != nil && !IsNil(o.Metadata) { return true } return false } -// SetManifest gets a reference to the given map[string]interface{} and assigns it to the Manifest field. -func (o *Resource) SetManifest(v map[string]interface{}) { - o.Manifest = v +// SetMetadata gets a reference to the given map[string]interface{} and assigns it to the Metadata field. +func (o *Resource) SetMetadata(v map[string]interface{}) { + o.Metadata = v } -// GetGroupResource returns the GroupResource field value if set, zero value otherwise. -func (o *Resource) GetGroupResource() map[string]interface{} { - if o == nil || IsNil(o.GroupResource) { - var ret map[string]interface{} +// GetManifests returns the Manifests field value if set, zero value otherwise. +func (o *Resource) GetManifests() []map[string]interface{} { + if o == nil || IsNil(o.Manifests) { + var ret []map[string]interface{} return ret } - return o.GroupResource + return o.Manifests } -// GetGroupResourceOk returns a tuple with the GroupResource field value if set, nil otherwise +// GetManifestsOk returns a tuple with the Manifests field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *Resource) GetGroupResourceOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.GroupResource) { - return map[string]interface{}{}, false +func (o *Resource) GetManifestsOk() ([]map[string]interface{}, bool) { + if o == nil || IsNil(o.Manifests) { + return nil, false } - return o.GroupResource, true + return o.Manifests, true } -// HasGroupResource returns a boolean if a field has been set. -func (o *Resource) HasGroupResource() bool { - if o != nil && !IsNil(o.GroupResource) { +// HasManifests returns a boolean if a field has been set. +func (o *Resource) HasManifests() bool { + if o != nil && !IsNil(o.Manifests) { return true } return false } -// SetGroupResource gets a reference to the given map[string]interface{} and assigns it to the GroupResource field. -func (o *Resource) SetGroupResource(v map[string]interface{}) { - o.GroupResource = v +// SetManifests gets a reference to the given []map[string]interface{} and assigns it to the Manifests field. +func (o *Resource) SetManifests(v []map[string]interface{}) { + o.Manifests = v } // GetDeleteOption returns the DeleteOption field value if set, zero value otherwise. @@ -437,36 +437,36 @@ func (o *Resource) SetDeleteOption(v map[string]interface{}) { o.DeleteOption = v } -// GetUpdateStrategy returns the UpdateStrategy field value if set, zero value otherwise. -func (o *Resource) GetUpdateStrategy() map[string]interface{} { - if o == nil || IsNil(o.UpdateStrategy) { - var ret map[string]interface{} +// GetManifestConfigs returns the ManifestConfigs field value if set, zero value otherwise. +func (o *Resource) GetManifestConfigs() []map[string]interface{} { + if o == nil || IsNil(o.ManifestConfigs) { + var ret []map[string]interface{} return ret } - return o.UpdateStrategy + return o.ManifestConfigs } -// GetUpdateStrategyOk returns a tuple with the UpdateStrategy field value if set, nil otherwise +// GetManifestConfigsOk returns a tuple with the ManifestConfigs field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *Resource) GetUpdateStrategyOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.UpdateStrategy) { - return map[string]interface{}{}, false +func (o *Resource) GetManifestConfigsOk() ([]map[string]interface{}, bool) { + if o == nil || IsNil(o.ManifestConfigs) { + return nil, false } - return o.UpdateStrategy, true + return o.ManifestConfigs, true } -// HasUpdateStrategy returns a boolean if a field has been set. -func (o *Resource) HasUpdateStrategy() bool { - if o != nil && !IsNil(o.UpdateStrategy) { +// HasManifestConfigs returns a boolean if a field has been set. +func (o *Resource) HasManifestConfigs() bool { + if o != nil && !IsNil(o.ManifestConfigs) { return true } return false } -// SetUpdateStrategy gets a reference to the given map[string]interface{} and assigns it to the UpdateStrategy field. -func (o *Resource) SetUpdateStrategy(v map[string]interface{}) { - o.UpdateStrategy = v +// SetManifestConfigs gets a reference to the given []map[string]interface{} and assigns it to the ManifestConfigs field. +func (o *Resource) SetManifestConfigs(v []map[string]interface{}) { + o.ManifestConfigs = v } // GetStatus returns the Status field value if set, zero value otherwise. @@ -538,17 +538,17 @@ func (o Resource) ToMap() (map[string]interface{}, error) { if !IsNil(o.DeletedAt) { toSerialize["deleted_at"] = o.DeletedAt } - if !IsNil(o.Manifest) { - toSerialize["manifest"] = o.Manifest + if !IsNil(o.Metadata) { + toSerialize["metadata"] = o.Metadata } - if !IsNil(o.GroupResource) { - toSerialize["group_resource"] = o.GroupResource + if !IsNil(o.Manifests) { + toSerialize["manifests"] = o.Manifests } if !IsNil(o.DeleteOption) { toSerialize["delete_option"] = o.DeleteOption } - if !IsNil(o.UpdateStrategy) { - toSerialize["update_strategy"] = o.UpdateStrategy + if !IsNil(o.ManifestConfigs) { + toSerialize["manifest_configs"] = o.ManifestConfigs } if !IsNil(o.Status) { toSerialize["status"] = o.Status diff --git a/pkg/api/openapi/model_resource_all_of.go b/pkg/api/openapi/model_resource_all_of.go index 949fa4d1..427a4f49 100644 --- a/pkg/api/openapi/model_resource_all_of.go +++ b/pkg/api/openapi/model_resource_all_of.go @@ -20,17 +20,17 @@ var _ MappedNullable = &ResourceAllOf{} // ResourceAllOf struct for ResourceAllOf type ResourceAllOf struct { - Name *string `json:"name,omitempty"` - ConsumerName *string `json:"consumer_name,omitempty"` - Version *int32 `json:"version,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - DeletedAt *time.Time `json:"deleted_at,omitempty"` - Manifest map[string]interface{} `json:"manifest,omitempty"` - GroupResource map[string]interface{} `json:"group_resource,omitempty"` - DeleteOption map[string]interface{} `json:"delete_option,omitempty"` - UpdateStrategy map[string]interface{} `json:"update_strategy,omitempty"` - Status map[string]interface{} `json:"status,omitempty"` + Name *string `json:"name,omitempty"` + ConsumerName *string `json:"consumer_name,omitempty"` + Version *int32 `json:"version,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + DeletedAt *time.Time `json:"deleted_at,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + Manifests []map[string]interface{} `json:"manifests,omitempty"` + DeleteOption map[string]interface{} `json:"delete_option,omitempty"` + ManifestConfigs []map[string]interface{} `json:"manifest_configs,omitempty"` + Status map[string]interface{} `json:"status,omitempty"` } // NewResourceAllOf instantiates a new ResourceAllOf object @@ -242,68 +242,68 @@ func (o *ResourceAllOf) SetDeletedAt(v time.Time) { o.DeletedAt = &v } -// GetManifest returns the Manifest field value if set, zero value otherwise. -func (o *ResourceAllOf) GetManifest() map[string]interface{} { - if o == nil || IsNil(o.Manifest) { +// GetMetadata returns the Metadata field value if set, zero value otherwise. +func (o *ResourceAllOf) GetMetadata() map[string]interface{} { + if o == nil || IsNil(o.Metadata) { var ret map[string]interface{} return ret } - return o.Manifest + return o.Metadata } -// GetManifestOk returns a tuple with the Manifest field value if set, nil otherwise +// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *ResourceAllOf) GetManifestOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Manifest) { +func (o *ResourceAllOf) GetMetadataOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Metadata) { return map[string]interface{}{}, false } - return o.Manifest, true + return o.Metadata, true } -// HasManifest returns a boolean if a field has been set. -func (o *ResourceAllOf) HasManifest() bool { - if o != nil && !IsNil(o.Manifest) { +// HasMetadata returns a boolean if a field has been set. +func (o *ResourceAllOf) HasMetadata() bool { + if o != nil && !IsNil(o.Metadata) { return true } return false } -// SetManifest gets a reference to the given map[string]interface{} and assigns it to the Manifest field. -func (o *ResourceAllOf) SetManifest(v map[string]interface{}) { - o.Manifest = v +// SetMetadata gets a reference to the given map[string]interface{} and assigns it to the Metadata field. +func (o *ResourceAllOf) SetMetadata(v map[string]interface{}) { + o.Metadata = v } -// GetGroupResource returns the GroupResource field value if set, zero value otherwise. -func (o *ResourceAllOf) GetGroupResource() map[string]interface{} { - if o == nil || IsNil(o.GroupResource) { - var ret map[string]interface{} +// GetManifests returns the Manifests field value if set, zero value otherwise. +func (o *ResourceAllOf) GetManifests() []map[string]interface{} { + if o == nil || IsNil(o.Manifests) { + var ret []map[string]interface{} return ret } - return o.GroupResource + return o.Manifests } -// GetGroupResourceOk returns a tuple with the GroupResource field value if set, nil otherwise +// GetManifestsOk returns a tuple with the Manifests field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *ResourceAllOf) GetGroupResourceOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.GroupResource) { - return map[string]interface{}{}, false +func (o *ResourceAllOf) GetManifestsOk() ([]map[string]interface{}, bool) { + if o == nil || IsNil(o.Manifests) { + return nil, false } - return o.GroupResource, true + return o.Manifests, true } -// HasGroupResource returns a boolean if a field has been set. -func (o *ResourceAllOf) HasGroupResource() bool { - if o != nil && !IsNil(o.GroupResource) { +// HasManifests returns a boolean if a field has been set. +func (o *ResourceAllOf) HasManifests() bool { + if o != nil && !IsNil(o.Manifests) { return true } return false } -// SetGroupResource gets a reference to the given map[string]interface{} and assigns it to the GroupResource field. -func (o *ResourceAllOf) SetGroupResource(v map[string]interface{}) { - o.GroupResource = v +// SetManifests gets a reference to the given []map[string]interface{} and assigns it to the Manifests field. +func (o *ResourceAllOf) SetManifests(v []map[string]interface{}) { + o.Manifests = v } // GetDeleteOption returns the DeleteOption field value if set, zero value otherwise. @@ -338,36 +338,36 @@ func (o *ResourceAllOf) SetDeleteOption(v map[string]interface{}) { o.DeleteOption = v } -// GetUpdateStrategy returns the UpdateStrategy field value if set, zero value otherwise. -func (o *ResourceAllOf) GetUpdateStrategy() map[string]interface{} { - if o == nil || IsNil(o.UpdateStrategy) { - var ret map[string]interface{} +// GetManifestConfigs returns the ManifestConfigs field value if set, zero value otherwise. +func (o *ResourceAllOf) GetManifestConfigs() []map[string]interface{} { + if o == nil || IsNil(o.ManifestConfigs) { + var ret []map[string]interface{} return ret } - return o.UpdateStrategy + return o.ManifestConfigs } -// GetUpdateStrategyOk returns a tuple with the UpdateStrategy field value if set, nil otherwise +// GetManifestConfigsOk returns a tuple with the ManifestConfigs field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *ResourceAllOf) GetUpdateStrategyOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.UpdateStrategy) { - return map[string]interface{}{}, false +func (o *ResourceAllOf) GetManifestConfigsOk() ([]map[string]interface{}, bool) { + if o == nil || IsNil(o.ManifestConfigs) { + return nil, false } - return o.UpdateStrategy, true + return o.ManifestConfigs, true } -// HasUpdateStrategy returns a boolean if a field has been set. -func (o *ResourceAllOf) HasUpdateStrategy() bool { - if o != nil && !IsNil(o.UpdateStrategy) { +// HasManifestConfigs returns a boolean if a field has been set. +func (o *ResourceAllOf) HasManifestConfigs() bool { + if o != nil && !IsNil(o.ManifestConfigs) { return true } return false } -// SetUpdateStrategy gets a reference to the given map[string]interface{} and assigns it to the UpdateStrategy field. -func (o *ResourceAllOf) SetUpdateStrategy(v map[string]interface{}) { - o.UpdateStrategy = v +// SetManifestConfigs gets a reference to the given []map[string]interface{} and assigns it to the ManifestConfigs field. +func (o *ResourceAllOf) SetManifestConfigs(v []map[string]interface{}) { + o.ManifestConfigs = v } // GetStatus returns the Status field value if set, zero value otherwise. @@ -430,17 +430,17 @@ func (o ResourceAllOf) ToMap() (map[string]interface{}, error) { if !IsNil(o.DeletedAt) { toSerialize["deleted_at"] = o.DeletedAt } - if !IsNil(o.Manifest) { - toSerialize["manifest"] = o.Manifest + if !IsNil(o.Metadata) { + toSerialize["metadata"] = o.Metadata } - if !IsNil(o.GroupResource) { - toSerialize["group_resource"] = o.GroupResource + if !IsNil(o.Manifests) { + toSerialize["manifests"] = o.Manifests } if !IsNil(o.DeleteOption) { toSerialize["delete_option"] = o.DeleteOption } - if !IsNil(o.UpdateStrategy) { - toSerialize["update_strategy"] = o.UpdateStrategy + if !IsNil(o.ManifestConfigs) { + toSerialize["manifest_configs"] = o.ManifestConfigs } if !IsNil(o.Status) { toSerialize["status"] = o.Status diff --git a/pkg/api/openapi/model_resource_bundle_all_of.go b/pkg/api/openapi/model_resource_bundle_all_of.go deleted file mode 100644 index f6d84ef5..00000000 --- a/pkg/api/openapi/model_resource_bundle_all_of.go +++ /dev/null @@ -1,485 +0,0 @@ -/* -maestro Service API - -maestro Service API - -API version: 0.0.1 -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package openapi - -import ( - "encoding/json" - "time" -) - -// checks if the ResourceBundleAllOf type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ResourceBundleAllOf{} - -// ResourceBundleAllOf struct for ResourceBundleAllOf -type ResourceBundleAllOf struct { - Name *string `json:"name,omitempty"` - ConsumerName *string `json:"consumer_name,omitempty"` - Version *int32 `json:"version,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - DeletedAt *time.Time `json:"deleted_at,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - Manifests []map[string]interface{} `json:"manifests,omitempty"` - DeleteOption map[string]interface{} `json:"delete_option,omitempty"` - ManifestConfigs []map[string]interface{} `json:"manifest_configs,omitempty"` - Status map[string]interface{} `json:"status,omitempty"` -} - -// NewResourceBundleAllOf instantiates a new ResourceBundleAllOf object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewResourceBundleAllOf() *ResourceBundleAllOf { - this := ResourceBundleAllOf{} - return &this -} - -// NewResourceBundleAllOfWithDefaults instantiates a new ResourceBundleAllOf object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewResourceBundleAllOfWithDefaults() *ResourceBundleAllOf { - this := ResourceBundleAllOf{} - return &this -} - -// GetName returns the Name field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetName() string { - if o == nil || IsNil(o.Name) { - var ret string - return ret - } - return *o.Name -} - -// GetNameOk returns a tuple with the Name field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetNameOk() (*string, bool) { - if o == nil || IsNil(o.Name) { - return nil, false - } - return o.Name, true -} - -// HasName returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasName() bool { - if o != nil && !IsNil(o.Name) { - return true - } - - return false -} - -// SetName gets a reference to the given string and assigns it to the Name field. -func (o *ResourceBundleAllOf) SetName(v string) { - o.Name = &v -} - -// GetConsumerName returns the ConsumerName field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetConsumerName() string { - if o == nil || IsNil(o.ConsumerName) { - var ret string - return ret - } - return *o.ConsumerName -} - -// GetConsumerNameOk returns a tuple with the ConsumerName field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetConsumerNameOk() (*string, bool) { - if o == nil || IsNil(o.ConsumerName) { - return nil, false - } - return o.ConsumerName, true -} - -// HasConsumerName returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasConsumerName() bool { - if o != nil && !IsNil(o.ConsumerName) { - return true - } - - return false -} - -// SetConsumerName gets a reference to the given string and assigns it to the ConsumerName field. -func (o *ResourceBundleAllOf) SetConsumerName(v string) { - o.ConsumerName = &v -} - -// GetVersion returns the Version field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetVersion() int32 { - if o == nil || IsNil(o.Version) { - var ret int32 - return ret - } - return *o.Version -} - -// GetVersionOk returns a tuple with the Version field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetVersionOk() (*int32, bool) { - if o == nil || IsNil(o.Version) { - return nil, false - } - return o.Version, true -} - -// HasVersion returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasVersion() bool { - if o != nil && !IsNil(o.Version) { - return true - } - - return false -} - -// SetVersion gets a reference to the given int32 and assigns it to the Version field. -func (o *ResourceBundleAllOf) SetVersion(v int32) { - o.Version = &v -} - -// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetCreatedAt() time.Time { - if o == nil || IsNil(o.CreatedAt) { - var ret time.Time - return ret - } - return *o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetCreatedAtOk() (*time.Time, bool) { - if o == nil || IsNil(o.CreatedAt) { - return nil, false - } - return o.CreatedAt, true -} - -// HasCreatedAt returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasCreatedAt() bool { - if o != nil && !IsNil(o.CreatedAt) { - return true - } - - return false -} - -// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field. -func (o *ResourceBundleAllOf) SetCreatedAt(v time.Time) { - o.CreatedAt = &v -} - -// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetUpdatedAt() time.Time { - if o == nil || IsNil(o.UpdatedAt) { - var ret time.Time - return ret - } - return *o.UpdatedAt -} - -// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetUpdatedAtOk() (*time.Time, bool) { - if o == nil || IsNil(o.UpdatedAt) { - return nil, false - } - return o.UpdatedAt, true -} - -// HasUpdatedAt returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasUpdatedAt() bool { - if o != nil && !IsNil(o.UpdatedAt) { - return true - } - - return false -} - -// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field. -func (o *ResourceBundleAllOf) SetUpdatedAt(v time.Time) { - o.UpdatedAt = &v -} - -// GetDeletedAt returns the DeletedAt field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetDeletedAt() time.Time { - if o == nil || IsNil(o.DeletedAt) { - var ret time.Time - return ret - } - return *o.DeletedAt -} - -// GetDeletedAtOk returns a tuple with the DeletedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetDeletedAtOk() (*time.Time, bool) { - if o == nil || IsNil(o.DeletedAt) { - return nil, false - } - return o.DeletedAt, true -} - -// HasDeletedAt returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasDeletedAt() bool { - if o != nil && !IsNil(o.DeletedAt) { - return true - } - - return false -} - -// SetDeletedAt gets a reference to the given time.Time and assigns it to the DeletedAt field. -func (o *ResourceBundleAllOf) SetDeletedAt(v time.Time) { - o.DeletedAt = &v -} - -// GetMetadata returns the Metadata field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetMetadata() map[string]interface{} { - if o == nil || IsNil(o.Metadata) { - var ret map[string]interface{} - return ret - } - return o.Metadata -} - -// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetMetadataOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Metadata) { - return map[string]interface{}{}, false - } - return o.Metadata, true -} - -// HasMetadata returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasMetadata() bool { - if o != nil && !IsNil(o.Metadata) { - return true - } - - return false -} - -// SetMetadata gets a reference to the given map[string]interface{} and assigns it to the Metadata field. -func (o *ResourceBundleAllOf) SetMetadata(v map[string]interface{}) { - o.Metadata = v -} - -// GetManifests returns the Manifests field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetManifests() []map[string]interface{} { - if o == nil || IsNil(o.Manifests) { - var ret []map[string]interface{} - return ret - } - return o.Manifests -} - -// GetManifestsOk returns a tuple with the Manifests field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetManifestsOk() ([]map[string]interface{}, bool) { - if o == nil || IsNil(o.Manifests) { - return nil, false - } - return o.Manifests, true -} - -// HasManifests returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasManifests() bool { - if o != nil && !IsNil(o.Manifests) { - return true - } - - return false -} - -// SetManifests gets a reference to the given []map[string]interface{} and assigns it to the Manifests field. -func (o *ResourceBundleAllOf) SetManifests(v []map[string]interface{}) { - o.Manifests = v -} - -// GetDeleteOption returns the DeleteOption field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetDeleteOption() map[string]interface{} { - if o == nil || IsNil(o.DeleteOption) { - var ret map[string]interface{} - return ret - } - return o.DeleteOption -} - -// GetDeleteOptionOk returns a tuple with the DeleteOption field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetDeleteOptionOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.DeleteOption) { - return map[string]interface{}{}, false - } - return o.DeleteOption, true -} - -// HasDeleteOption returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasDeleteOption() bool { - if o != nil && !IsNil(o.DeleteOption) { - return true - } - - return false -} - -// SetDeleteOption gets a reference to the given map[string]interface{} and assigns it to the DeleteOption field. -func (o *ResourceBundleAllOf) SetDeleteOption(v map[string]interface{}) { - o.DeleteOption = v -} - -// GetManifestConfigs returns the ManifestConfigs field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetManifestConfigs() []map[string]interface{} { - if o == nil || IsNil(o.ManifestConfigs) { - var ret []map[string]interface{} - return ret - } - return o.ManifestConfigs -} - -// GetManifestConfigsOk returns a tuple with the ManifestConfigs field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetManifestConfigsOk() ([]map[string]interface{}, bool) { - if o == nil || IsNil(o.ManifestConfigs) { - return nil, false - } - return o.ManifestConfigs, true -} - -// HasManifestConfigs returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasManifestConfigs() bool { - if o != nil && !IsNil(o.ManifestConfigs) { - return true - } - - return false -} - -// SetManifestConfigs gets a reference to the given []map[string]interface{} and assigns it to the ManifestConfigs field. -func (o *ResourceBundleAllOf) SetManifestConfigs(v []map[string]interface{}) { - o.ManifestConfigs = v -} - -// GetStatus returns the Status field value if set, zero value otherwise. -func (o *ResourceBundleAllOf) GetStatus() map[string]interface{} { - if o == nil || IsNil(o.Status) { - var ret map[string]interface{} - return ret - } - return o.Status -} - -// GetStatusOk returns a tuple with the Status field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourceBundleAllOf) GetStatusOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Status) { - return map[string]interface{}{}, false - } - return o.Status, true -} - -// HasStatus returns a boolean if a field has been set. -func (o *ResourceBundleAllOf) HasStatus() bool { - if o != nil && !IsNil(o.Status) { - return true - } - - return false -} - -// SetStatus gets a reference to the given map[string]interface{} and assigns it to the Status field. -func (o *ResourceBundleAllOf) SetStatus(v map[string]interface{}) { - o.Status = v -} - -func (o ResourceBundleAllOf) MarshalJSON() ([]byte, error) { - toSerialize, err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ResourceBundleAllOf) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Name) { - toSerialize["name"] = o.Name - } - if !IsNil(o.ConsumerName) { - toSerialize["consumer_name"] = o.ConsumerName - } - if !IsNil(o.Version) { - toSerialize["version"] = o.Version - } - if !IsNil(o.CreatedAt) { - toSerialize["created_at"] = o.CreatedAt - } - if !IsNil(o.UpdatedAt) { - toSerialize["updated_at"] = o.UpdatedAt - } - if !IsNil(o.DeletedAt) { - toSerialize["deleted_at"] = o.DeletedAt - } - if !IsNil(o.Metadata) { - toSerialize["metadata"] = o.Metadata - } - if !IsNil(o.Manifests) { - toSerialize["manifests"] = o.Manifests - } - if !IsNil(o.DeleteOption) { - toSerialize["delete_option"] = o.DeleteOption - } - if !IsNil(o.ManifestConfigs) { - toSerialize["manifest_configs"] = o.ManifestConfigs - } - if !IsNil(o.Status) { - toSerialize["status"] = o.Status - } - return toSerialize, nil -} - -type NullableResourceBundleAllOf struct { - value *ResourceBundleAllOf - isSet bool -} - -func (v NullableResourceBundleAllOf) Get() *ResourceBundleAllOf { - return v.value -} - -func (v *NullableResourceBundleAllOf) Set(val *ResourceBundleAllOf) { - v.value = val - v.isSet = true -} - -func (v NullableResourceBundleAllOf) IsSet() bool { - return v.isSet -} - -func (v *NullableResourceBundleAllOf) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableResourceBundleAllOf(val *ResourceBundleAllOf) *NullableResourceBundleAllOf { - return &NullableResourceBundleAllOf{value: val, isSet: true} -} - -func (v NullableResourceBundleAllOf) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableResourceBundleAllOf) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/pkg/api/openapi/model_resource_patch_request.go b/pkg/api/openapi/model_resource_patch_request.go deleted file mode 100644 index a72ea467..00000000 --- a/pkg/api/openapi/model_resource_patch_request.go +++ /dev/null @@ -1,232 +0,0 @@ -/* -maestro Service API - -maestro Service API - -API version: 0.0.1 -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package openapi - -import ( - "encoding/json" -) - -// checks if the ResourcePatchRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ResourcePatchRequest{} - -// ResourcePatchRequest struct for ResourcePatchRequest -type ResourcePatchRequest struct { - Version *int32 `json:"version,omitempty"` - Manifest map[string]interface{} `json:"manifest,omitempty"` - DeleteOption map[string]interface{} `json:"delete_option,omitempty"` - UpdateStrategy map[string]interface{} `json:"update_strategy,omitempty"` -} - -// NewResourcePatchRequest instantiates a new ResourcePatchRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewResourcePatchRequest() *ResourcePatchRequest { - this := ResourcePatchRequest{} - return &this -} - -// NewResourcePatchRequestWithDefaults instantiates a new ResourcePatchRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewResourcePatchRequestWithDefaults() *ResourcePatchRequest { - this := ResourcePatchRequest{} - return &this -} - -// GetVersion returns the Version field value if set, zero value otherwise. -func (o *ResourcePatchRequest) GetVersion() int32 { - if o == nil || IsNil(o.Version) { - var ret int32 - return ret - } - return *o.Version -} - -// GetVersionOk returns a tuple with the Version field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourcePatchRequest) GetVersionOk() (*int32, bool) { - if o == nil || IsNil(o.Version) { - return nil, false - } - return o.Version, true -} - -// HasVersion returns a boolean if a field has been set. -func (o *ResourcePatchRequest) HasVersion() bool { - if o != nil && !IsNil(o.Version) { - return true - } - - return false -} - -// SetVersion gets a reference to the given int32 and assigns it to the Version field. -func (o *ResourcePatchRequest) SetVersion(v int32) { - o.Version = &v -} - -// GetManifest returns the Manifest field value if set, zero value otherwise. -func (o *ResourcePatchRequest) GetManifest() map[string]interface{} { - if o == nil || IsNil(o.Manifest) { - var ret map[string]interface{} - return ret - } - return o.Manifest -} - -// GetManifestOk returns a tuple with the Manifest field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourcePatchRequest) GetManifestOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Manifest) { - return map[string]interface{}{}, false - } - return o.Manifest, true -} - -// HasManifest returns a boolean if a field has been set. -func (o *ResourcePatchRequest) HasManifest() bool { - if o != nil && !IsNil(o.Manifest) { - return true - } - - return false -} - -// SetManifest gets a reference to the given map[string]interface{} and assigns it to the Manifest field. -func (o *ResourcePatchRequest) SetManifest(v map[string]interface{}) { - o.Manifest = v -} - -// GetDeleteOption returns the DeleteOption field value if set, zero value otherwise. -func (o *ResourcePatchRequest) GetDeleteOption() map[string]interface{} { - if o == nil || IsNil(o.DeleteOption) { - var ret map[string]interface{} - return ret - } - return o.DeleteOption -} - -// GetDeleteOptionOk returns a tuple with the DeleteOption field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourcePatchRequest) GetDeleteOptionOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.DeleteOption) { - return map[string]interface{}{}, false - } - return o.DeleteOption, true -} - -// HasDeleteOption returns a boolean if a field has been set. -func (o *ResourcePatchRequest) HasDeleteOption() bool { - if o != nil && !IsNil(o.DeleteOption) { - return true - } - - return false -} - -// SetDeleteOption gets a reference to the given map[string]interface{} and assigns it to the DeleteOption field. -func (o *ResourcePatchRequest) SetDeleteOption(v map[string]interface{}) { - o.DeleteOption = v -} - -// GetUpdateStrategy returns the UpdateStrategy field value if set, zero value otherwise. -func (o *ResourcePatchRequest) GetUpdateStrategy() map[string]interface{} { - if o == nil || IsNil(o.UpdateStrategy) { - var ret map[string]interface{} - return ret - } - return o.UpdateStrategy -} - -// GetUpdateStrategyOk returns a tuple with the UpdateStrategy field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResourcePatchRequest) GetUpdateStrategyOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.UpdateStrategy) { - return map[string]interface{}{}, false - } - return o.UpdateStrategy, true -} - -// HasUpdateStrategy returns a boolean if a field has been set. -func (o *ResourcePatchRequest) HasUpdateStrategy() bool { - if o != nil && !IsNil(o.UpdateStrategy) { - return true - } - - return false -} - -// SetUpdateStrategy gets a reference to the given map[string]interface{} and assigns it to the UpdateStrategy field. -func (o *ResourcePatchRequest) SetUpdateStrategy(v map[string]interface{}) { - o.UpdateStrategy = v -} - -func (o ResourcePatchRequest) MarshalJSON() ([]byte, error) { - toSerialize, err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ResourcePatchRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Version) { - toSerialize["version"] = o.Version - } - if !IsNil(o.Manifest) { - toSerialize["manifest"] = o.Manifest - } - if !IsNil(o.DeleteOption) { - toSerialize["delete_option"] = o.DeleteOption - } - if !IsNil(o.UpdateStrategy) { - toSerialize["update_strategy"] = o.UpdateStrategy - } - return toSerialize, nil -} - -type NullableResourcePatchRequest struct { - value *ResourcePatchRequest - isSet bool -} - -func (v NullableResourcePatchRequest) Get() *ResourcePatchRequest { - return v.value -} - -func (v *NullableResourcePatchRequest) Set(val *ResourcePatchRequest) { - v.value = val - v.isSet = true -} - -func (v NullableResourcePatchRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableResourcePatchRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableResourcePatchRequest(val *ResourcePatchRequest) *NullableResourcePatchRequest { - return &NullableResourcePatchRequest{value: val, isSet: true} -} - -func (v NullableResourcePatchRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableResourcePatchRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/pkg/api/presenters/resource.go b/pkg/api/presenters/resource.go old mode 100755 new mode 100644 index 0663b312..7397a4b8 --- a/pkg/api/presenters/resource.go +++ b/pkg/api/presenters/resource.go @@ -1,42 +1,53 @@ package presenters import ( - "gorm.io/datatypes" + "fmt" "github.com/openshift-online/maestro/pkg/api" "github.com/openshift-online/maestro/pkg/api/openapi" - "github.com/openshift-online/maestro/pkg/constants" - "github.com/openshift-online/maestro/pkg/util" ) -// ConvertResource converts a resource from the API to the openapi representation. -func ConvertResource(resource openapi.Resource) (*api.Resource, error) { - payload, err := ConvertResourceManifest(resource.Manifest, resource.GroupResource, resource.DeleteOption, resource.UpdateStrategy) +// PresentResource converts a resource from the API to the openapi representation. +func PresentResource(resource *api.Resource) (*openapi.Resource, error) { + manifestWrapper, err := api.DecodeManifest(resource.Payload) if err != nil { return nil, err } - return &api.Resource{ - Name: util.NilToEmptyString(resource.Name), - Meta: api.Meta{ - ID: util.NilToEmptyString(resource.Id), - }, - ConsumerName: util.NilToEmptyString(resource.ConsumerName), - Version: util.NilToEmptyInt32(resource.Version), - // Set the default source ID for RESTful API calls and do not allow modification - Source: constants.DefaultSourceID, - Type: api.ResourceTypeSingle, - Payload: payload, - }, nil -} + status, err := api.DecodeStatus(resource.Status) + if err != nil { + return nil, err + } + + res := &openapi.Resource{ + Id: openapi.PtrString(resource.ID), + Kind: openapi.PtrString("Resource"), + Href: openapi.PtrString(fmt.Sprintf("%s/%s/%s", BasePath, "resources", resource.ID)), + Name: openapi.PtrString(resource.Name), + ConsumerName: openapi.PtrString(resource.ConsumerName), + Version: openapi.PtrInt32(resource.Version), + CreatedAt: openapi.PtrTime(resource.CreatedAt), + UpdatedAt: openapi.PtrTime(resource.UpdatedAt), + Status: status, + } + + if manifestWrapper != nil { + res.Metadata = manifestWrapper.Meta + res.Manifests = manifestWrapper.Manifests + res.ManifestConfigs = manifestWrapper.ManifestConfigs + res.DeleteOption = manifestWrapper.DeleteOption + } + + // set the deletedAt field if the resource has been marked as deleted + if !resource.DeletedAt.Time.IsZero() { + res.DeletedAt = openapi.PtrTime(resource.DeletedAt.Time) + } -// ConvertResourceManifest converts a resource manifest from the openapi representation to the API. -func ConvertResourceManifest(manifest, groupResource, deleteOption, updateStrategy map[string]interface{}) (datatypes.JSONMap, error) { - return api.EncodeManifest(manifest, groupResource, deleteOption, updateStrategy) + return res, nil } -// PresentResource converts a resource from the API to the openapi representation. -func PresentResource(resource *api.Resource) (*openapi.Resource, error) { - manifest, groupResource, deleteOption, updateStrategy, err := api.DecodeManifest(resource.Payload) +// PresentResourceBundle converts a resource from the API to the openapi representation. +func PresentResourceBundle(resource *api.Resource) (*openapi.ResourceBundle, error) { + manifestWrapper, err := api.DecodeManifest(resource.Payload) if err != nil { return nil, err } @@ -44,27 +55,30 @@ func PresentResource(resource *api.Resource) (*openapi.Resource, error) { if err != nil { return nil, err } - reference := PresentReference(resource.ID, resource) - res := &openapi.Resource{ - Id: reference.Id, - Kind: reference.Kind, - Href: reference.Href, - Name: openapi.PtrString(resource.Name), - ConsumerName: openapi.PtrString(resource.ConsumerName), - Version: openapi.PtrInt32(resource.Version), - CreatedAt: openapi.PtrTime(resource.CreatedAt), - UpdatedAt: openapi.PtrTime(resource.UpdatedAt), - Manifest: manifest, - GroupResource: groupResource, - DeleteOption: deleteOption, - UpdateStrategy: updateStrategy, - Status: status, + + rb := &openapi.ResourceBundle{ + Id: openapi.PtrString(resource.ID), + Kind: openapi.PtrString("ResourceBundle"), + Href: openapi.PtrString(fmt.Sprintf("%s/%s/%s", BasePath, "resource-bundles", resource.ID)), + Name: openapi.PtrString(resource.Name), + ConsumerName: openapi.PtrString(resource.ConsumerName), + Version: openapi.PtrInt32(resource.Version), + CreatedAt: openapi.PtrTime(resource.CreatedAt), + UpdatedAt: openapi.PtrTime(resource.UpdatedAt), + Status: status, + } + + if manifestWrapper != nil { + rb.Metadata = manifestWrapper.Meta + rb.Manifests = manifestWrapper.Manifests + rb.ManifestConfigs = manifestWrapper.ManifestConfigs + rb.DeleteOption = manifestWrapper.DeleteOption } // set the deletedAt field if the resource has been marked as deleted if !resource.DeletedAt.Time.IsZero() { - res.DeletedAt = openapi.PtrTime(resource.DeletedAt.Time) + rb.DeletedAt = openapi.PtrTime(resource.DeletedAt.Time) } - return res, nil + return rb, nil } diff --git a/pkg/api/presenters/resource_bundle.go b/pkg/api/presenters/resource_bundle.go deleted file mode 100644 index 737659e2..00000000 --- a/pkg/api/presenters/resource_bundle.go +++ /dev/null @@ -1,46 +0,0 @@ -package presenters - -import ( - "fmt" - - "github.com/openshift-online/maestro/pkg/api" - "github.com/openshift-online/maestro/pkg/api/openapi" -) - -// PresentResourceBundle converts a resource from the API to the openapi representation. -func PresentResourceBundle(resource *api.Resource) (*openapi.ResourceBundle, error) { - manifestBundle, err := api.DecodeManifestBundle(resource.Payload) - if err != nil { - return nil, err - } - status, err := api.DecodeBundleStatus(resource.Status) - if err != nil { - return nil, err - } - - res := &openapi.ResourceBundle{ - Id: openapi.PtrString(resource.ID), - Kind: openapi.PtrString("ResourceBundle"), - Href: openapi.PtrString(fmt.Sprintf("%s/%s/%s", BasePath, "resource-bundles", resource.ID)), - Name: openapi.PtrString(resource.Name), - ConsumerName: openapi.PtrString(resource.ConsumerName), - Version: openapi.PtrInt32(resource.Version), - CreatedAt: openapi.PtrTime(resource.CreatedAt), - UpdatedAt: openapi.PtrTime(resource.UpdatedAt), - Status: status, - } - - if manifestBundle != nil { - res.Metadata = manifestBundle.Meta - res.Manifests = manifestBundle.Manifests - res.ManifestConfigs = manifestBundle.ManifestConfigs - res.DeleteOption = manifestBundle.DeleteOption - } - - // set the deletedAt field if the resource has been marked as deleted - if !resource.DeletedAt.Time.IsZero() { - res.DeletedAt = openapi.PtrTime(resource.DeletedAt.Time) - } - - return res, nil -} diff --git a/pkg/api/resource.go b/pkg/api/resource.go new file mode 100755 index 00000000..41da0a7d --- /dev/null +++ b/pkg/api/resource.go @@ -0,0 +1,216 @@ +package api + +import ( + "encoding/json" + "fmt" + + cloudevents "github.com/cloudevents/sdk-go/v2" + cloudeventstypes "github.com/cloudevents/sdk-go/v2/types" + "gorm.io/datatypes" + + "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/types" + workpayload "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" + "open-cluster-management.io/sdk-go/pkg/cloudevents/work/source/codec" +) + +// ResourceStatus defines resource status +type ResourceStatus struct { + ObservedVersion int32 + SequenceID string + *workpayload.ManifestBundleStatus +} + +// ManifestWrapper is a wrapper used for openapi output that contains: +// * metadata - manifestwork metadata +// * manifests - resource manifests +// * manifest configs - manifest configs +// * delete option - delete option +type ManifestWrapper struct { + Meta map[string]interface{} + Manifests []map[string]interface{} + ManifestConfigs []map[string]interface{} + DeleteOption map[string]interface{} +} + +// DecodeManifest converts a CloudEvent JSONMap representation of a list of resource manifest +// into manifests and manifestconfigs that will be used in openapi output. +func DecodeManifest(manifest datatypes.JSONMap) (*ManifestWrapper, error) { + if len(manifest) == 0 { + return nil, nil + } + + evt, err := JSONMAPToCloudEvent(manifest) + if err != nil { + return nil, fmt.Errorf("failed to convert resource manifest to cloudevent: %v", err) + } + + metaData := map[string]any{} + extensions := evt.Extensions() + if meta, ok := extensions[codec.ExtensionWorkMeta]; ok { + metaJson, err := cloudeventstypes.ToString(meta) + if err != nil { + return nil, fmt.Errorf("failed to get work meta extension: %v", err) + } + + if err := json.Unmarshal([]byte(metaJson), &metaData); err != nil { + return nil, fmt.Errorf("failed to unmarshal work meta extension: %v", err) + } + } + + eventPayload := &workpayload.ManifestBundle{} + if err := evt.DataAs(eventPayload); err != nil { + return nil, fmt.Errorf("failed to decode cloudevent payload: %v", err) + } + + manifests := make([]map[string]interface{}, 0, len(eventPayload.Manifests)) + for _, manifest := range eventPayload.Manifests { + m := map[string]interface{}{} + if err := json.Unmarshal(manifest.Raw, &m); err != nil { + return nil, fmt.Errorf("failed to unmarshal manifest raw: %v", err) + } + manifests = append(manifests, m) + } + manifestConfigs := make([]map[string]interface{}, 0, len(eventPayload.ManifestConfigs)) + for _, manifestConfig := range eventPayload.ManifestConfigs { + mbytes, err := json.Marshal(manifestConfig) + if err != nil { + return nil, fmt.Errorf("failed to marshal manifest config: %v", err) + } + m := map[string]interface{}{} + if err := json.Unmarshal(mbytes, &m); err != nil { + return nil, fmt.Errorf("failed to unmarshal manifest config: %v", err) + } + manifestConfigs = append(manifestConfigs, m) + } + deleteOption := map[string]interface{}{} + if eventPayload.DeleteOption != nil { + dbytes, err := json.Marshal(eventPayload.DeleteOption) + if err != nil { + return nil, fmt.Errorf("failed to marshal delete option: %v", err) + } + if err := json.Unmarshal(dbytes, &deleteOption); err != nil { + return nil, fmt.Errorf("failed to unmarshal delete option: %v", err) + } + } + + return &ManifestWrapper{ + Meta: metaData, + Manifests: manifests, + ManifestConfigs: manifestConfigs, + DeleteOption: deleteOption, + }, nil +} + +// DecodeStatus converts a CloudEvent JSONMap representation of a resource status +// into resource status (map[string]interface{}) in openapi output. +func DecodeStatus(status datatypes.JSONMap) (map[string]interface{}, error) { + if len(status) == 0 { + return nil, nil + } + + evt, err := JSONMAPToCloudEvent(status) + if err != nil { + return nil, fmt.Errorf("failed to convert resource status to cloudevent: %v", err) + } + + evtExtensions := evt.Extensions() + resourceVersion, err := cloudeventstypes.ToInteger(evtExtensions[types.ExtensionResourceVersion]) + if err != nil { + return nil, fmt.Errorf("failed to get resourceversion extension: %v", err) + } + + sequenceID, err := cloudeventstypes.ToString(evtExtensions[types.ExtensionStatusUpdateSequenceID]) + if err != nil { + return nil, fmt.Errorf("failed to get sequenceid extension: %v", err) + } + + resourceStatus := &ResourceStatus{ + ObservedVersion: resourceVersion, + SequenceID: sequenceID, + ManifestBundleStatus: &workpayload.ManifestBundleStatus{}, + } + if err := evt.DataAs(resourceStatus.ManifestBundleStatus); err != nil { + return nil, fmt.Errorf("failed to decode cloudevent payload: %v", err) + } + resourceStatusJSON, err := json.Marshal(resourceStatus) + if err != nil { + return nil, fmt.Errorf("failed to marshal resource status: %v", err) + } + statusMap := make(map[string]interface{}) + if err := json.Unmarshal(resourceStatusJSON, &statusMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal resource status: %v", err) + } + + return statusMap, nil +} + +// JSONMAPToCloudEvent converts a JSONMap (resource manifest or status) to a CloudEvent +func JSONMAPToCloudEvent(jsonmap datatypes.JSONMap) (*cloudevents.Event, error) { + if len(jsonmap) == 0 { + return nil, fmt.Errorf("failed to convert empty jsonmap to cloudevent") + } + + var err error + var resJSON []byte + + if metadata, ok := jsonmap[codec.ExtensionWorkMeta]; ok { + // cloudevents require its extension value as string, so we need convert the metadata object + // to string back + + // ensure the original resource will be not changed + resCopy := datatypes.JSONMap{} + for key, value := range jsonmap { + resCopy[key] = value + } + + metaJson, err := json.Marshal(metadata) + if err != nil { + return nil, fmt.Errorf("failed to marshal metadata to JSON: %v", err) + } + + resCopy[codec.ExtensionWorkMeta] = string(metaJson) + + resJSON, err = resCopy.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal JSONMAP to cloudevent JSON: %v", err) + } + } else { + resJSON, err = jsonmap.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal JSONMAP to cloudevent JSON: %v", err) + } + } + + evt := &cloudevents.Event{} + if err := json.Unmarshal(resJSON, evt); err != nil { + return nil, fmt.Errorf("failed to unmarshal JSONMAP to cloudevent: %v", err) + } + + return evt, nil +} + +// CloudEventToJSONMap converts a CloudEvent to a JSONMap (resource manifest or status) +func CloudEventToJSONMap(evt *cloudevents.Event) (datatypes.JSONMap, error) { + evtJSON, err := json.Marshal(evt) + if err != nil { + return nil, fmt.Errorf("failed to marshal cloudevent to JSONMAP: %v", err) + } + + var res datatypes.JSONMap + if err := res.UnmarshalJSON(evtJSON); err != nil { + return nil, fmt.Errorf("failed to unmarshal cloudevent JSON to JSONMAP: %v", err) + } + + if metadata, ok := res[codec.ExtensionWorkMeta]; ok { + // cloudevents treat its extension value as string, so we need convert metadata extension + // to an object for supporting to query the resources with metadata + objectMeta := map[string]any{} + + if err := json.Unmarshal([]byte(fmt.Sprintf("%s", metadata)), &objectMeta); err != nil { + return nil, fmt.Errorf("failed to unmarshal metadata extension to object: %v", err) + } + res[codec.ExtensionWorkMeta] = objectMeta + } + + return res, nil +} diff --git a/pkg/api/resource_bundle_types.go b/pkg/api/resource_bundle_types.go deleted file mode 100755 index 2c1d7e50..00000000 --- a/pkg/api/resource_bundle_types.go +++ /dev/null @@ -1,146 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - - cloudeventstypes "github.com/cloudevents/sdk-go/v2/types" - "gorm.io/datatypes" - - cetypes "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/types" - workpayload "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" - "open-cluster-management.io/sdk-go/pkg/cloudevents/work/source/codec" -) - -type ResourceBundleStatus struct { - ObservedVersion int32 - SequenceID string - *workpayload.ManifestBundleStatus -} - -// ManifestBundleWrapper is a wrapper used for openapi output that contains: -// * metadata - manifestwork metadata -// * manifests - resource manifests -// * manifest configs - manifest configs -// * delete option - delete option -type ManifestBundleWrapper struct { - Meta map[string]interface{} - Manifests []map[string]interface{} - ManifestConfigs []map[string]interface{} - DeleteOption map[string]interface{} -} - -// DecodeManifestBundle converts a CloudEvent JSONMap representation of a list of resource manifest -// into ManifestBundle that will be used in openapi output. -func DecodeManifestBundle(manifest datatypes.JSONMap) (*ManifestBundleWrapper, error) { - if len(manifest) == 0 { - return nil, nil - } - - evt, err := JSONMAPToCloudEvent(manifest) - if err != nil { - return nil, fmt.Errorf("failed to convert resource manifest bundle to cloudevent: %v", err) - } - - metaData := map[string]any{} - extensions := evt.Extensions() - if meta, ok := extensions[codec.ExtensionWorkMeta]; ok { - metaJson, err := cloudeventstypes.ToString(meta) - if err != nil { - return nil, fmt.Errorf("failed to get work meta extension: %v", err) - } - - if err := json.Unmarshal([]byte(metaJson), &metaData); err != nil { - return nil, fmt.Errorf("failed to unmarshal work meta extension: %v", err) - } - } - - eventPayload := &workpayload.ManifestBundle{} - if err := evt.DataAs(eventPayload); err != nil { - return nil, fmt.Errorf("failed to decode cloudevent payload as resource manifest bundle: %v", err) - } - - manifests := make([]map[string]interface{}, 0, len(eventPayload.Manifests)) - for _, manifest := range eventPayload.Manifests { - m := map[string]interface{}{} - if err := json.Unmarshal(manifest.Raw, &m); err != nil { - return nil, fmt.Errorf("failed to unmarshal manifest raw in bundle: %v", err) - } - manifests = append(manifests, m) - } - manifestConfigs := make([]map[string]interface{}, 0, len(eventPayload.ManifestConfigs)) - for _, manifestConfig := range eventPayload.ManifestConfigs { - mbytes, err := json.Marshal(manifestConfig) - if err != nil { - return nil, fmt.Errorf("failed to marshal manifest config in bundle: %v", err) - } - m := map[string]interface{}{} - if err := json.Unmarshal(mbytes, &m); err != nil { - return nil, fmt.Errorf("failed to unmarshal manifest config in bundle: %v", err) - } - manifestConfigs = append(manifestConfigs, m) - } - deleteOption := map[string]interface{}{} - if eventPayload.DeleteOption != nil { - dbytes, err := json.Marshal(eventPayload.DeleteOption) - if err != nil { - return nil, fmt.Errorf("failed to marshal delete option in bundle: %v", err) - } - if err := json.Unmarshal(dbytes, &deleteOption); err != nil { - return nil, fmt.Errorf("failed to unmarshal delete option in bundle: %v", err) - } - } - - return &ManifestBundleWrapper{ - Meta: metaData, - Manifests: manifests, - ManifestConfigs: manifestConfigs, - DeleteOption: deleteOption, - }, nil -} - -// DecodeBundleStatus converts a CloudEvent JSONMap representation of a resource bundle status -// into resource bundle status (map[string]interface{}) in openapi output. -func DecodeBundleStatus(status datatypes.JSONMap) (map[string]interface{}, error) { - if len(status) == 0 { - return nil, nil - } - - evt, err := JSONMAPToCloudEvent(status) - if err != nil { - return nil, fmt.Errorf("failed to convert resource bundle status to cloudevent: %v", err) - } - - evtExtensions := evt.Extensions() - resourceVersion, err := cloudeventstypes.ToInteger(evtExtensions[cetypes.ExtensionResourceVersion]) - if err != nil { - return nil, fmt.Errorf("failed to get resourceversion extension: %v", err) - } - - sequenceID, err := cloudeventstypes.ToString(evtExtensions[cetypes.ExtensionStatusUpdateSequenceID]) - if err != nil { - return nil, fmt.Errorf("failed to get sequenceid extension: %v", err) - } - - resourceBundleStatus := &ResourceBundleStatus{ - ObservedVersion: resourceVersion, - SequenceID: sequenceID, - } - - eventPayload := &workpayload.ManifestBundleStatus{} - if err := evt.DataAs(eventPayload); err != nil { - return nil, fmt.Errorf("failed to decode cloudevent data as resource bundle status: %v", err) - } - resourceBundleStatus.ManifestBundleStatus = eventPayload - - resourceBundleStatusJSON, err := json.Marshal(resourceBundleStatus) - if err != nil { - return nil, fmt.Errorf("failed to marshal resource bundle status to JSON: %v", err) - } - statusMap := make(map[string]interface{}) - if err := json.Unmarshal(resourceBundleStatusJSON, &statusMap); err != nil { - return nil, fmt.Errorf("failed to unmarshal resource bundle status JSON to map: %v", err) - } - - return statusMap, nil -} diff --git a/pkg/api/resource_bundle_types_test.go b/pkg/api/resource_test.go similarity index 89% rename from pkg/api/resource_bundle_types_test.go rename to pkg/api/resource_test.go index ff298783..17b67344 100644 --- a/pkg/api/resource_bundle_types_test.go +++ b/pkg/api/resource_test.go @@ -8,7 +8,7 @@ import ( "k8s.io/apimachinery/pkg/api/equality" ) -func TestDecodeManifestBundle(t *testing.T) { +func TestDecodeManifest(t *testing.T) { cases := []struct { name string input datatypes.JSONMap @@ -46,32 +46,32 @@ func TestDecodeManifestBundle(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { - gotManifestBundle, err := DecodeManifestBundle(c.input) + gotManifest, err := DecodeManifest(c.input) if err != nil { if err.Error() != c.expectedErrorMsg { t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) } return } - if gotManifestBundle != nil { - if !equality.Semantic.DeepEqual(c.expectedMetaData, gotManifestBundle.Meta) { - t.Errorf("expected metaData %#v but got: %#v", c.expectedMetaData, gotManifestBundle.Meta) + if gotManifest != nil { + if !equality.Semantic.DeepEqual(c.expectedMetaData, gotManifest.Meta) { + t.Errorf("expected metaData %#v but got: %#v", c.expectedMetaData, gotManifest.Meta) } - if !equality.Semantic.DeepEqual(c.expectedManifests, gotManifestBundle.Manifests) { - t.Errorf("expected manifests %#v but got: %#v", c.expectedManifests, gotManifestBundle.Manifests) + if !equality.Semantic.DeepEqual(c.expectedManifests, gotManifest.Manifests) { + t.Errorf("expected manifests %#v but got: %#v", c.expectedManifests, gotManifest.Manifests) } - if !equality.Semantic.DeepEqual(c.expectedManifestConfigs, gotManifestBundle.ManifestConfigs) { - t.Errorf("expected manifestConfigs %#v but got: %#v", c.expectedManifestConfigs, gotManifestBundle.ManifestConfigs) + if !equality.Semantic.DeepEqual(c.expectedManifestConfigs, gotManifest.ManifestConfigs) { + t.Errorf("expected manifestConfigs %#v but got: %#v", c.expectedManifestConfigs, gotManifest.ManifestConfigs) } - if !equality.Semantic.DeepEqual(c.expectedDeleteOption, gotManifestBundle.DeleteOption) { - t.Errorf("expected deleteOption %#v but got: %#v", c.expectedDeleteOption, gotManifestBundle.DeleteOption) + if !equality.Semantic.DeepEqual(c.expectedDeleteOption, gotManifest.DeleteOption) { + t.Errorf("expected deleteOption %#v but got: %#v", c.expectedDeleteOption, gotManifest.DeleteOption) } } }) } } -func TestDecodeBundleStatus(t *testing.T) { +func TestDecodeStatus(t *testing.T) { cases := []struct { name string input datatypes.JSONMap @@ -94,22 +94,40 @@ func TestDecodeBundleStatus(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { - got, err := DecodeBundleStatus(c.input) + gotStatus, err := DecodeStatus(c.input) if err != nil { if err.Error() != c.expectedErrorMsg { t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) } return } - gotBytes, err := json.Marshal(got) + gotStatusBytes, err := json.Marshal(gotStatus) if err != nil { - t.Errorf("failed to marshal got resource bundle status: %v", err) + t.Errorf("failed to marshal got resource status: %v", err) } - if string(gotBytes) != c.expectedJSON { - t.Errorf("expected %s but got: %s", c.expectedJSON, string(gotBytes)) + if string(gotStatusBytes) != c.expectedJSON { + t.Errorf("expected status %s but got status: %s", c.expectedJSON, string(gotStatusBytes)) } }) } } + +func newJSONMap(t *testing.T, data string) datatypes.JSONMap { + jsonmap := map[string]interface{}{} + if err := json.Unmarshal([]byte(data), &jsonmap); err != nil { + t.Fatal(err) + } + + return jsonmap +} + +func newJSONMAPList(t *testing.T, data ...string) []map[string]any { + jsonmapList := []map[string]any{} + for _, d := range data { + jsonmapList = append(jsonmapList, newJSONMap(t, d)) + } + + return jsonmapList +} diff --git a/pkg/api/resource_types.go b/pkg/api/resource_types.go index d3eef734..7dde3656 100755 --- a/pkg/api/resource_types.go +++ b/pkg/api/resource_types.go @@ -1,32 +1,16 @@ package api import ( - "encoding/json" - "fmt" "strconv" - cloudevents "github.com/cloudevents/sdk-go/v2" - cloudeventstypes "github.com/cloudevents/sdk-go/v2/types" "gorm.io/datatypes" "gorm.io/gorm" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" ktypes "k8s.io/apimachinery/pkg/types" - - workv1 "open-cluster-management.io/api/work/v1" - cetypes "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/types" - workpayload "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" - "open-cluster-management.io/sdk-go/pkg/cloudevents/work/source/codec" ) type ResourceType string -const ( - ResourceTypeSingle ResourceType = "Single" - ResourceTypeBundle ResourceType = "Bundle" -) - type Resource struct { Meta Version int32 @@ -42,17 +26,6 @@ type Resource struct { Name string } -type ResourceStatus struct { - ContentStatus datatypes.JSONMap - ReconcileStatus *ReconcileStatus -} - -type ReconcileStatus struct { - ObservedVersion int32 - SequenceID string - Conditions []metav1.Condition -} - type ResourceList []*Resource type ResourceIndex map[string]*Resource @@ -90,286 +63,3 @@ func (d *Resource) GetResourceVersion() string { func (d *Resource) GetDeletionTimestamp() *metav1.Time { return &metav1.Time{Time: d.Meta.DeletedAt.Time} } - -type ResourcePatchRequest struct{} - -// JSONMAPToCloudEvent converts a JSONMap (resource manifest or status) to a CloudEvent -func JSONMAPToCloudEvent(res datatypes.JSONMap) (*cloudevents.Event, error) { - var err error - var resJSON []byte - - if metadata, ok := res[codec.ExtensionWorkMeta]; ok { - // cloudevents require its extension value as string, so we need convert the metadata object - // to string back - - // ensure the original resource will be not changed - resCopy := datatypes.JSONMap{} - for key, value := range res { - resCopy[key] = value - } - - metaJson, err := json.Marshal(metadata) - if err != nil { - return nil, fmt.Errorf("failed to marshal metadata to JSON: %v", err) - } - - resCopy[codec.ExtensionWorkMeta] = string(metaJson) - - resJSON, err = resCopy.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("failed to marshal JSONMAP to cloudevent JSON: %v", err) - } - } else { - resJSON, err = res.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("failed to marshal JSONMAP to cloudevent JSON: %v", err) - } - } - - evt := &cloudevents.Event{} - if err := json.Unmarshal(resJSON, evt); err != nil { - return nil, fmt.Errorf("failed to unmarshal JSONMAP to cloudevent: %v", err) - } - - return evt, nil -} - -// CloudEventToJSONMap converts a CloudEvent to a JSONMap (resource manifest or status) -func CloudEventToJSONMap(evt *cloudevents.Event) (datatypes.JSONMap, error) { - evtJSON, err := json.Marshal(evt) - if err != nil { - return nil, fmt.Errorf("failed to marshal cloudevent to JSONMAP: %v", err) - } - - var res datatypes.JSONMap - if err := res.UnmarshalJSON(evtJSON); err != nil { - return nil, fmt.Errorf("failed to unmarshal cloudevent JSON to JSONMAP: %v", err) - } - - if metadata, ok := res[codec.ExtensionWorkMeta]; ok { - // cloudevents treat its extension value as string, so we need convert metadata extension - // to an object for supporting to query the resources with metadata - objectMeta := map[string]any{} - - if err := json.Unmarshal([]byte(fmt.Sprintf("%s", metadata)), &objectMeta); err != nil { - return nil, fmt.Errorf("failed to unmarshal metadata extension to object: %v", err) - } - res[codec.ExtensionWorkMeta] = objectMeta - } - - return res, nil -} - -// EncodeManifest converts resource manifest, groupResource, deleteOption and updateStrategy (map[string]interface{}) into a CloudEvent JSONMap representation. -func EncodeManifest(manifest, groupResource, deleteOption, updateStrategy map[string]interface{}) (datatypes.JSONMap, error) { - if len(manifest) == 0 { - return nil, nil - } - - // default update strategy is ServerSideApply - upStrategy := &workv1.UpdateStrategy{ - Type: workv1.UpdateStrategyTypeServerSideApply, - } - if len(updateStrategy) != 0 { - updateStrategyBytes, err := json.Marshal(updateStrategy) - if err != nil { - return nil, fmt.Errorf("failed to marshal updateStrategy to json: %v", err) - } - err = json.Unmarshal(updateStrategyBytes, upStrategy) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal json to updateStrategy: %v", err) - } - } - - // default delete option is Foreground - delOption := &workv1.DeleteOption{ - PropagationPolicy: workv1.DeletePropagationPolicyTypeForeground, - } - - // set delete option to Orphan if update strategy is ReadOnly - if upStrategy.Type == workv1.UpdateStrategyTypeReadOnly { - delOption = &workv1.DeleteOption{ - PropagationPolicy: workv1.DeletePropagationPolicyTypeOrphan, - } - } else { - if len(deleteOption) != 0 { - deleteOptionBytes, err := json.Marshal(deleteOption) - if err != nil { - return nil, fmt.Errorf("failed to marshal deleteOption to json: %v", err) - } - err = json.Unmarshal(deleteOptionBytes, delOption) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal json to deleteOption: %v", err) - } - } - } - - // create a cloud event with the manifest as the data - evt := cetypes.NewEventBuilder("maestro", cetypes.CloudEventsType{}).NewEvent() - manifestBytes, err := json.Marshal(manifest) - if err != nil { - return nil, fmt.Errorf("failed to marshal manifest: %v", err) - } - unstructuredObj := &unstructured.Unstructured{ - Object: manifest, - } - - eventPayload := &workpayload.ManifestBundle{ - Manifests: []workv1.Manifest{ - { - RawExtension: runtime.RawExtension{Raw: manifestBytes}, - }, - }, - DeleteOption: delOption, - ManifestConfigs: []workv1.ManifestConfigOption{ - { - ResourceIdentifier: workv1.ResourceIdentifier{ - Group: groupResource["group"].(string), - Resource: groupResource["resource"].(string), - Name: unstructuredObj.GetName(), - Namespace: unstructuredObj.GetNamespace(), - }, - FeedbackRules: []workv1.FeedbackRule{ - { - Type: workv1.JSONPathsType, - JsonPaths: []workv1.JsonPath{ - { - Name: "status", - Path: ".status", - }, - }, - }, - }, - UpdateStrategy: upStrategy, - }, - }, - } - - if err := evt.SetData(cloudevents.ApplicationJSON, eventPayload); err != nil { - return nil, fmt.Errorf("failed to set cloud event data: %v", err) - } - - // convert cloudevent to JSONMap - manifest, err = CloudEventToJSONMap(&evt) - if err != nil { - return nil, fmt.Errorf("failed to convert cloudevent to resource manifest: %v", err) - } - - return manifest, nil -} - -// DecodeManifest converts a CloudEvent JSONMap representation of a resource manifest into resource -// manifest, groupResource, deleteOption and updateStrategy (map[string]interface{}) for openapi output. -func DecodeManifest(manifest datatypes.JSONMap) (map[string]interface{}, map[string]interface{}, map[string]interface{}, map[string]interface{}, error) { - if len(manifest) == 0 { - return nil, nil, nil, nil, nil - } - - evt, err := JSONMAPToCloudEvent(manifest) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to convert resource manifest to cloudevent: %v", err) - } - - eventPayload := &workpayload.ManifestBundle{} - if err := evt.DataAs(eventPayload); err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to decode cloudevent payload as resource manifest: %v", err) - } - - deleteOptionObj := map[string]interface{}{} - if eventPayload.DeleteOption != nil { - deleteOptionBytes, err := json.Marshal(eventPayload.DeleteOption) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to marshal deleteOption to json: %v", err) - } - if err := json.Unmarshal(deleteOptionBytes, &deleteOptionObj); err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to unmarshal deleteOption to cloudevent: %v", err) - } - } - manifestObj := map[string]interface{}{} - if len(eventPayload.Manifests) != 1 { - return nil, nil, nil, nil, fmt.Errorf("invalid number of manifests in the event payload: %d", len(eventPayload.Manifests)) - } - if err := json.Unmarshal(eventPayload.Manifests[0].Raw, &manifestObj); err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to unmarshal manifest raw to manifest: %v", err) - } - if len(eventPayload.ManifestConfigs) != 1 { - return nil, nil, nil, nil, fmt.Errorf("invalid number of manifestConfigs in the event payload: %d", len(eventPayload.ManifestConfigs)) - } - resourceGroup := map[string]interface{}{ - "group": eventPayload.ManifestConfigs[0].ResourceIdentifier.Group, - "resource": eventPayload.ManifestConfigs[0].ResourceIdentifier.Resource, - } - updateStrategyObj := map[string]interface{}{} - if eventPayload.ManifestConfigs[0].UpdateStrategy != nil { - updateStrategyBytes, err := json.Marshal(eventPayload.ManifestConfigs[0].UpdateStrategy) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to marshal updateStrategy to json: %v", err) - } - if err := json.Unmarshal(updateStrategyBytes, &updateStrategyObj); err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to unmarshal updateStrategy json to object: %v", err) - } - } - - return manifestObj, resourceGroup, deleteOptionObj, updateStrategyObj, nil -} - -// DecodeStatus converts a CloudEvent JSONMap representation of a resource status -// into resource status (map[string]interface{}). -func DecodeStatus(status datatypes.JSONMap) (map[string]interface{}, error) { - if len(status) == 0 { - return nil, nil - } - - evt, err := JSONMAPToCloudEvent(status) - if err != nil { - return nil, fmt.Errorf("failed to convert resource status to cloudevent: %v", err) - } - - evtExtensions := evt.Extensions() - resourceVersion, err := cloudeventstypes.ToInteger(evtExtensions[cetypes.ExtensionResourceVersion]) - if err != nil { - return nil, fmt.Errorf("failed to get resourceversion extension: %v", err) - } - - sequenceID, err := cloudeventstypes.ToString(evtExtensions[cetypes.ExtensionStatusUpdateSequenceID]) - if err != nil { - return nil, fmt.Errorf("failed to get sequenceid extension: %v", err) - } - - resourceStatus := &ResourceStatus{ - ReconcileStatus: &ReconcileStatus{ - ObservedVersion: resourceVersion, - SequenceID: sequenceID, - }, - } - - eventPayload := &workpayload.ManifestBundleStatus{} - if err := evt.DataAs(eventPayload); err != nil { - return nil, fmt.Errorf("failed to decode cloudevent data as resource status: %v", err) - } - - if len(eventPayload.ResourceStatus) != 1 { - return nil, fmt.Errorf("invalid number of resource status in the event payload: %d", len(eventPayload.ResourceStatus)) - } - resourceStatus.ReconcileStatus.Conditions = eventPayload.ResourceStatus[0].Conditions - for _, value := range eventPayload.ResourceStatus[0].StatusFeedbacks.Values { - if value.Name == "status" { - contentStatus := make(map[string]interface{}) - if err := json.Unmarshal([]byte(*value.Value.JsonRaw), &contentStatus); err != nil { - return nil, fmt.Errorf("failed to convert status feedback value to content status: %v", err) - } - resourceStatus.ContentStatus = contentStatus - } - } - - resourceStatusJSON, err := json.Marshal(resourceStatus) - if err != nil { - return nil, fmt.Errorf("failed to marshal resource status to JSON: %v", err) - } - statusMap := make(map[string]interface{}) - if err := json.Unmarshal(resourceStatusJSON, &statusMap); err != nil { - return nil, fmt.Errorf("failed to unmarshal resource status JSON to object: %v", err) - } - - return statusMap, nil -} diff --git a/pkg/api/resource_types_test.go b/pkg/api/resource_types_test.go deleted file mode 100644 index f45dd779..00000000 --- a/pkg/api/resource_types_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package api - -import ( - "encoding/json" - "testing" - - "gorm.io/datatypes" - "k8s.io/apimachinery/pkg/api/equality" -) - -func TestEncodeManifest(t *testing.T) { - cases := []struct { - name string - manifest map[string]interface{} - groupResource map[string]interface{} - deleteOption map[string]interface{} - updateStrategy map[string]interface{} - expected datatypes.JSONMap - expectedErrorMsg string - }{ - { - name: "empty", - manifest: map[string]interface{}{}, - expected: datatypes.JSONMap{}, - }, - { - name: "valid", - manifest: newJSONMap(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"), - groupResource: newJSONMap(t, "{\"group\":\"\",\"resource\":\"configmaps\"}"), - expected: newJSONMap(t, "{\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"feedbackRules\":[{\"jsonPaths\":[{\"name\":\"status\",\"path\":\".status\"}],\"type\":\"JSONPaths\"}],\"resourceIdentifier\":{\"group\":\"\",\"name\":\"test\",\"namespace\":\"test\",\"resource\":\"configmaps\"},\"updateStrategy\":{\"type\":\"ServerSideApply\"}}],\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}]}}"), - }, - { - name: "valid", - deleteOption: newJSONMap(t, "{\"propagationPolicy\": \"Orphan\"}"), - manifest: newJSONMap(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"), - groupResource: newJSONMap(t, "{\"group\":\"\",\"resource\":\"configmaps\"}"), - updateStrategy: newJSONMap(t, "{\"type\": \"CreateOnly\"}"), - expected: newJSONMap(t, "{\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"deleteOption\":{\"propagationPolicy\":\"Orphan\"},\"manifestConfigs\":[{\"feedbackRules\":[{\"jsonPaths\":[{\"name\":\"status\",\"path\":\".status\"}],\"type\":\"JSONPaths\"}],\"resourceIdentifier\":{\"group\":\"\",\"name\":\"test\",\"namespace\":\"test\",\"resource\":\"configmaps\"},\"updateStrategy\":{\"type\":\"CreateOnly\"}}],\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}]}}"), - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - gotManifest, err := EncodeManifest(c.manifest, c.groupResource, c.deleteOption, c.updateStrategy) - if err != nil { - if err.Error() != c.expectedErrorMsg { - t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) - } - return - } - if !equality.Semantic.DeepDerivative(c.expected, gotManifest) { - t.Errorf("expected %#v but got: %#v", c.expected, gotManifest) - } - }) - } -} - -func TestDecodeManifest(t *testing.T) { - cases := []struct { - name string - input datatypes.JSONMap - expectedManifest map[string]interface{} - expectedGroupResource map[string]interface{} - expectedDeleteOption map[string]interface{} - expectedUpdateStrategy map[string]interface{} - expectedErrorMsg string - }{ - { - name: "empty", - input: datatypes.JSONMap{}, - expectedManifest: nil, - expectedDeleteOption: nil, - expectedUpdateStrategy: nil, - expectedErrorMsg: "", - }, - { - name: "valid", - input: newJSONMap(t, "{\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"deleteOption\":{\"propagationPolicy\":\"Orphan\"},\"manifestConfigs\":[{\"feedbackRules\":[{\"jsonPaths\":[{\"name\":\"status\",\"path\":\".status\"}],\"type\":\"JSONPaths\"}],\"resourceIdentifier\":{\"group\":\"\",\"name\":\"test\",\"namespace\":\"test\",\"resource\":\"configmaps\"},\"updateStrategy\":{\"type\":\"CreateOnly\"}}],\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}]}}"), - expectedManifest: newJSONMap(t, "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}"), - expectedGroupResource: newJSONMap(t, "{\"group\":\"\",\"resource\":\"configmaps\"}"), - expectedDeleteOption: newJSONMap(t, "{\"propagationPolicy\": \"Orphan\"}"), - expectedUpdateStrategy: newJSONMap(t, "{\"type\": \"CreateOnly\"}"), - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - gotManifest, gotGroupResource, gotDeleteOption, gotUpdateStrategy, err := DecodeManifest(c.input) - if err != nil { - if err.Error() != c.expectedErrorMsg { - t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) - } - return - } - if !equality.Semantic.DeepDerivative(c.expectedManifest, gotManifest) { - t.Errorf("expected %#v but got: %#v", c.expectedManifest, gotManifest) - } - if !equality.Semantic.DeepDerivative(c.expectedGroupResource, gotGroupResource) { - t.Errorf("expected %#v but got: %#v", c.expectedGroupResource, gotGroupResource) - } - if !equality.Semantic.DeepDerivative(c.expectedDeleteOption, gotDeleteOption) { - t.Errorf("expected %#v but got: %#v", c.expectedDeleteOption, gotDeleteOption) - } - if !equality.Semantic.DeepDerivative(c.expectedUpdateStrategy, gotUpdateStrategy) { - t.Errorf("expected %#v but got: %#v", c.expectedUpdateStrategy, gotUpdateStrategy) - } - }) - } -} - -func TestDecodeStatus(t *testing.T) { - cases := []struct { - name string - input datatypes.JSONMap - expected map[string]interface{} - expectedErrorMsg string - }{ - { - name: "empty", - input: datatypes.JSONMap{}, - expected: nil, - expectedErrorMsg: "", - }, - { - name: "valid", - input: newJSONMap(t, "{\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"id\":\"1f21fcbe-3e41-4639-ab8d-1713c578e4cd\",\"time\":\"2024-03-07T03:29:12.094854533Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifests.status.update_request\",\"source\":\"maestro-agent-59d9c485d9-7bvwb\",\"resourceid\":\"b9368296-3200-42ec-bfbb-f7d44a06c4e0\",\"sequenceid\":\"1765580430112722944\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"originalsource\":\"maestro\",\"resourceversion\":\"1\",\"data\":{\"conditions\":[{\"type\":\"Applied\",\"reason\":\"AppliedManifestWorkComplete\",\"status\":\"True\",\"message\":\"Apply manifest work complete\",\"lastTransitionTime\":\"2024-03-07T03:56:35Z\"},{\"type\":\"Available\",\"reason\":\"ResourcesAvailable\",\"status\":\"True\",\"message\":\"All resources are available\",\"lastTransitionTime\":\"2024-03-07T03:56:35Z\"}],\"resourceStatus\":[{\"conditions\":[{\"type\":\"Applied\",\"reason\":\"AppliedManifestComplete\",\"status\":\"True\",\"message\":\"Apply manifest complete\",\"lastTransitionTime\":\"2024-03-07T03:56:35Z\"},{\"type\":\"Available\",\"reason\":\"ResourceAvailable\",\"status\":\"True\",\"message\":\"Resource is available\",\"lastTransitionTime\":\"2024-03-07T03:56:35Z\"},{\"type\":\"StatusFeedbackSynced\",\"reason\":\"StatusFeedbackSynced\",\"status\":\"True\",\"message\":\"\",\"lastTransitionTime\":\"2024-03-07T03:56:35Z\"}],\"resourceMeta\":{\"kind\":\"Deployment\",\"name\":\"nginx\",\"group\":\"apps\",\"ordinal\":1,\"version\":\"v1\",\"resource\":\"deployments\",\"namespace\":\"default\"},\"statusFeedback\":{\"values\":[{\"name\":\"status\",\"fieldValue\":{\"type\":\"JsonRaw\",\"jsonRaw\":\"{\\\"availableReplicas\\\":2,\\\"conditions\\\":[{\\\"lastTransitionTime\\\":\\\"2024-03-07T03:56:35Z\\\",\\\"lastUpdateTime\\\":\\\"2024-03-07T03:56:38Z\\\",\\\"message\\\":\\\"ReplicaSet \\\\\\\"nginx-5d6b548959\\\\\\\" has successfully progressed.\\\",\\\"reason\\\":\\\"NewReplicaSetAvailable\\\",\\\"status\\\":\\\"True\\\",\\\"type\\\":\\\"Progressing\\\"},{\\\"lastTransitionTime\\\":\\\"2024-03-07T03:58: 26Z\\\",\\\"lastUpdateTime\\\":\\\"2024-03-07T03:58:26Z\\\",\\\"message\\\":\\\"Deployment has minimum availability.\\\",\\\"reason\\\":\\\"MinimumReplicasAvailable\\\",\\\"status\\\":\\\"True\\\",\\\"type\\\":\\\"Available\\\"}],\\\"observedGeneration\\\":2,\\\"readyReplicas\\\":2,\\\"replicas\\\":2,\\\"updatedReplicas\\\":2}\"}}]}}]}}"), - expected: newJSONMap(t, "{\"ContentStatus\":{\"availableReplicas\":2,\"conditions\":[{\"lastTransitionTime\":\"2024-03-07T03:56:35Z\",\"lastUpdateTime\":\"2024-03-07T03:56:38Z\",\"message\":\"ReplicaSet \\\"nginx-5d6b548959\\\" has successfully progressed.\",\"reason\":\"NewReplicaSetAvailable\",\"status\":\"True\",\"type\":\"Progressing\"},{\"lastTransitionTime\":\"2024-03-07T03:58: 26Z\",\"lastUpdateTime\":\"2024-03-07T03:58:26Z\",\"message\":\"Deployment has minimum availability.\",\"reason\":\"MinimumReplicasAvailable\",\"status\":\"True\",\"type\":\"Available\"}],\"observedGeneration\":2,\"readyReplicas\":2,\"replicas\":2,\"updatedReplicas\":2},\"ReconcileStatus\":{\"Conditions\":[{\"lastTransitionTime\":\"2024-03-07T03:56:35Z\",\"message\":\"Apply manifest complete\",\"reason\":\"AppliedManifestComplete\",\"status\":\"True\",\"type\":\"Applied\"},{\"lastTransitionTime\":\"2024-03-07T03:56:35Z\",\"message\":\"Resource is available\",\"reason\":\"ResourceAvailable\",\"status\":\"True\",\"type\":\"Available\"},{\"lastTransitionTime\":\"2024-03-07T03:56:35Z\",\"message\":\"\",\"reason\":\"StatusFeedbackSynced\",\"status\":\"True\",\"type\":\"StatusFeedbackSynced\"}],\"ObservedVersion\":1,\"SequenceID\":\"1765580430112722944\"}}"), - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - gotManifest, err := DecodeStatus(c.input) - if err != nil { - if err.Error() != c.expectedErrorMsg { - t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) - } - return - } - if !equality.Semantic.DeepDerivative(c.expected, gotManifest) { - t.Errorf("expected %#v but got: %#v", c.expected, gotManifest) - } - }) - } -} - -func newJSONMap(t *testing.T, data string) datatypes.JSONMap { - jsonmap := map[string]interface{}{} - if err := json.Unmarshal([]byte(data), &jsonmap); err != nil { - t.Fatal(err) - } - - return jsonmap -} - -func newJSONMAPList(t *testing.T, data ...string) []map[string]any { - jsonmapList := []map[string]any{} - for _, d := range data { - jsonmapList = append(jsonmapList, newJSONMap(t, d)) - } - - return jsonmapList -} diff --git a/pkg/client/cloudevents/codec.go b/pkg/client/cloudevents/codec.go index 51140704..75dd1117 100644 --- a/pkg/client/cloudevents/codec.go +++ b/pkg/client/cloudevents/codec.go @@ -20,6 +20,12 @@ type Codec struct { var _ cegeneric.Codec[*api.Resource] = &Codec{} +func NewCodec(sourceID string) *Codec { + return &Codec{ + sourceID: sourceID, + } +} + func (codec *Codec) EventDataType() cetypes.CloudEventsDataType { return workpayload.ManifestBundleEventDataType } diff --git a/pkg/client/cloudevents/grpcsource/mock/maestro.go b/pkg/client/cloudevents/grpcsource/mock/maestro.go index 41c097c2..8f474d9a 100644 --- a/pkg/client/cloudevents/grpcsource/mock/maestro.go +++ b/pkg/client/cloudevents/grpcsource/mock/maestro.go @@ -10,15 +10,15 @@ import ( "github.com/openshift-online/maestro/pkg/api/openapi" ) -type ResourceBundlesStore struct { - items []openapi.ResourceBundle +type ResourcesStore struct { + items []openapi.Resource } -func (g *ResourceBundlesStore) Get() []openapi.ResourceBundle { +func (g *ResourcesStore) Get() []openapi.Resource { return g.items } -func (g *ResourceBundlesStore) Set(items []openapi.ResourceBundle) { +func (g *ResourcesStore) Set(items []openapi.Resource) { g.items = items } @@ -26,13 +26,13 @@ type MaestroMockServer struct { server *httptest.Server } -func NewMaestroMockServer(store *ResourceBundlesStore) *MaestroMockServer { +func NewMaestroMockServer(store *ResourcesStore) *MaestroMockServer { mockServer := &MaestroMockServer{} handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - list := &openapi.ResourceBundleList{} + list := &openapi.ResourceList{} page, _ := strconv.Atoi(r.URL.Query().Get("page")) size, _ := strconv.Atoi(r.URL.Query().Get("size")) diff --git a/pkg/client/cloudevents/grpcsource/pager.go b/pkg/client/cloudevents/grpcsource/pager.go index 45df6d7f..1d824658 100644 --- a/pkg/client/cloudevents/grpcsource/pager.go +++ b/pkg/client/cloudevents/grpcsource/pager.go @@ -17,8 +17,8 @@ import ( var MaxListPageSize int32 = 400 // pageList assists client code in breaking large list queries into multiple smaller chunks of PageSize or smaller. -func pageList(ctx context.Context, client *openapi.APIClient, search string, opts metav1.ListOptions) (*openapi.ResourceBundleList, string, error) { - items := []openapi.ResourceBundle{} +func pageList(ctx context.Context, client *openapi.APIClient, search string, opts metav1.ListOptions) (*openapi.ResourceList, string, error) { + items := []openapi.Resource{} page, err := page(opts) if err != nil { @@ -36,7 +36,7 @@ func pageList(ctx context.Context, client *openapi.APIClient, search string, opt offset := (page - 1) * pageSize for { klog.V(4).Infof("list works with search=%s, page=%d, size=%d", search, page, pageSize) - rbs, _, err := client.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx). + rbs, _, err := client.DefaultApi.ApiMaestroV1ResourcesGet(ctx). Search(search). Page(page). Size(pageSize). @@ -71,7 +71,7 @@ func pageList(ctx context.Context, client *openapi.APIClient, search string, opt } } - return &openapi.ResourceBundleList{Items: items}, nextPage, nil + return &openapi.ResourceList{Items: items}, nextPage, nil } func page(opts metav1.ListOptions) (int32, error) { diff --git a/pkg/client/cloudevents/grpcsource/pager_test.go b/pkg/client/cloudevents/grpcsource/pager_test.go index e27550e8..1391d7d4 100644 --- a/pkg/client/cloudevents/grpcsource/pager_test.go +++ b/pkg/client/cloudevents/grpcsource/pager_test.go @@ -10,7 +10,7 @@ import ( ) func TestPageList(t *testing.T) { - getter := &mock.ResourceBundlesStore{} + getter := &mock.ResourcesStore{} maestroServer := mock.NewMaestroMockServer(getter) maestroServer.Start() defer maestroServer.Stop() @@ -18,42 +18,42 @@ func TestPageList(t *testing.T) { client := mock.NewMaestroAPIClient(maestroServer.URL()) cases := []struct { name string - resourceBundles []openapi.ResourceBundle + resources []openapi.Resource listOpts metav1.ListOptions expectedItemsLen int expectedNext string }{ { name: "no items", - resourceBundles: resourceBundles(0), + resources: resources(0), listOpts: metav1.ListOptions{}, expectedItemsLen: 0, expectedNext: "", }, { name: "list all items (items < MaxListPageSize)", - resourceBundles: resourceBundles(200), + resources: resources(200), listOpts: metav1.ListOptions{}, expectedItemsLen: 200, expectedNext: "", }, { name: "list all items (items = MaxListPageSize)", - resourceBundles: resourceBundles(400), + resources: resources(400), listOpts: metav1.ListOptions{}, expectedItemsLen: 400, expectedNext: "", }, { name: "list all items (items > MaxListPageSize)", - resourceBundles: resourceBundles(429), + resources: resources(429), listOpts: metav1.ListOptions{}, expectedItemsLen: 429, expectedNext: "", }, { - name: "list items (limit > total items)", - resourceBundles: resourceBundles(429), + name: "list items (limit > total items)", + resources: resources(429), listOpts: metav1.ListOptions{ Limit: 500, }, @@ -61,8 +61,8 @@ func TestPageList(t *testing.T) { expectedNext: "", }, { - name: "list items (limit < total items)", - resourceBundles: resourceBundles(429), + name: "list items (limit < total items)", + resources: resources(429), listOpts: metav1.ListOptions{ Limit: 400, }, @@ -70,8 +70,8 @@ func TestPageList(t *testing.T) { expectedNext: "2", }, { - name: "list items (limit < total items)", - resourceBundles: resourceBundles(429), + name: "list items (limit < total items)", + resources: resources(429), listOpts: metav1.ListOptions{ Limit: 40, }, @@ -79,8 +79,8 @@ func TestPageList(t *testing.T) { expectedNext: "2", }, { - name: "list items with continue (from last page - 1)", - resourceBundles: resourceBundles(429), + name: "list items with continue (from last page - 1)", + resources: resources(429), listOpts: metav1.ListOptions{ Limit: 100, Continue: "4", @@ -89,8 +89,8 @@ func TestPageList(t *testing.T) { expectedNext: "5", }, { - name: "list items with continue (from page last page)", - resourceBundles: resourceBundles(429), + name: "list items with continue (from page last page)", + resources: resources(429), listOpts: metav1.ListOptions{ Limit: 100, Continue: "5", @@ -99,8 +99,8 @@ func TestPageList(t *testing.T) { expectedNext: "", }, { - name: "list items with continue (from page last page + 1)", - resourceBundles: resourceBundles(429), + name: "list items with continue (from page last page + 1)", + resources: resources(429), listOpts: metav1.ListOptions{ Limit: 100, Continue: "6", @@ -109,8 +109,8 @@ func TestPageList(t *testing.T) { expectedNext: "", }, { - name: "list items with continue and max limit", - resourceBundles: resourceBundles(1229), + name: "list items with continue and max limit", + resources: resources(1229), listOpts: metav1.ListOptions{ Limit: 400, Continue: "3", @@ -119,8 +119,8 @@ func TestPageList(t *testing.T) { expectedNext: "4", }, { - name: "list items with continue and max limit", - resourceBundles: resourceBundles(1229), + name: "list items with continue and max limit", + resources: resources(1229), listOpts: metav1.ListOptions{ Limit: 400, Continue: "4", @@ -129,8 +129,8 @@ func TestPageList(t *testing.T) { expectedNext: "", }, { - name: "list items with continue and max limit", - resourceBundles: resourceBundles(1229), + name: "list items with continue and max limit", + resources: resources(1229), listOpts: metav1.ListOptions{ Limit: 400, Continue: "5", @@ -142,7 +142,7 @@ func TestPageList(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { - getter.Set(c.resourceBundles) + getter.Set(c.resources) list, next, err := pageList(context.Background(), client, "", c.listOpts) if err != nil { @@ -161,10 +161,10 @@ func TestPageList(t *testing.T) { } } -func resourceBundles(total int) []openapi.ResourceBundle { - items := []openapi.ResourceBundle{} +func resources(total int) []openapi.Resource { + items := []openapi.Resource{} for i := 0; i < total; i++ { - items = append(items, openapi.ResourceBundle{}) + items = append(items, openapi.Resource{}) } return items } diff --git a/pkg/client/cloudevents/grpcsource/util.go b/pkg/client/cloudevents/grpcsource/util.go index 7831c61b..0bfb9c20 100644 --- a/pkg/client/cloudevents/grpcsource/util.go +++ b/pkg/client/cloudevents/grpcsource/util.go @@ -18,8 +18,8 @@ import ( const jsonbPrefix = `payload->'metadata'->'labels'` -// ToManifestWork converts an openapi.ResourceBundle object to workv1.ManifestWork object -func ToManifestWork(rb *openapi.ResourceBundle) (*workv1.ManifestWork, error) { +// ToManifestWork converts an openapi.Resource object to workv1.ManifestWork object +func ToManifestWork(rb *openapi.Resource) (*workv1.ManifestWork, error) { work := &workv1.ManifestWork{} // get meta from resource diff --git a/pkg/client/cloudevents/grpcsource/util_test.go b/pkg/client/cloudevents/grpcsource/util_test.go index 8738d8b3..f8b6ce61 100644 --- a/pkg/client/cloudevents/grpcsource/util_test.go +++ b/pkg/client/cloudevents/grpcsource/util_test.go @@ -24,12 +24,12 @@ func TestToManifestWork(t *testing.T) { cases := []struct { name string - input *openapi.ResourceBundle + input *openapi.Resource expected *workv1.ManifestWork }{ { - name: "covert a resource bundle - has empty fields", - input: &openapi.ResourceBundle{ + name: "covert a resource - has empty fields", + input: &openapi.Resource{ Metadata: map[string]interface{}{ "name": "test", "namespace": "testns", @@ -62,8 +62,8 @@ func TestToManifestWork(t *testing.T) { }, }, { - name: "covert a resource bundle", - input: &openapi.ResourceBundle{ + name: "covert a resource", + input: &openapi.Resource{ Metadata: map[string]interface{}{ "name": "test", "namespace": "testns", diff --git a/pkg/client/cloudevents/grpcsource/watcherstore.go b/pkg/client/cloudevents/grpcsource/watcherstore.go index 892a1bed..6cc53370 100644 --- a/pkg/client/cloudevents/grpcsource/watcherstore.go +++ b/pkg/client/cloudevents/grpcsource/watcherstore.go @@ -128,7 +128,7 @@ func (m *RESTFulAPIWatcherStore) HandleReceivedWork(action types.ResourceAction, // Get a work from maestro server with its namespace and name func (m *RESTFulAPIWatcherStore) Get(namespace, name string) (*workv1.ManifestWork, bool, error) { id := utils.UID(m.sourceID, namespace, name) - rb, resp, err := m.apiClient.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(m.ctx, id).Execute() + rb, resp, err := m.apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(m.ctx, id).Execute() if err != nil { if resp != nil && resp.StatusCode == http.StatusNotFound { return nil, false, nil diff --git a/pkg/client/cloudevents/source_client.go b/pkg/client/cloudevents/source_client.go index 3205fee7..6fa781c0 100644 --- a/pkg/client/cloudevents/source_client.go +++ b/pkg/client/cloudevents/source_client.go @@ -37,7 +37,7 @@ type SourceClientImpl struct { func NewSourceClient(sourceOptions *ceoptions.CloudEventsSourceOptions, resourceService services.ResourceService) (SourceClient, error) { ctx := context.Background() - codec := &Codec{sourceID: sourceOptions.SourceID} + codec := NewCodec(sourceOptions.SourceID) ceSourceClient, err := cegeneric.NewCloudEventSourceClient[*api.Resource](ctx, sourceOptions, resourceService, ResourceStatusHashGetter, codec) if err != nil { diff --git a/pkg/client/cloudevents/source_client_mock.go b/pkg/client/cloudevents/source_client_mock.go index d8566099..9d7030d2 100644 --- a/pkg/client/cloudevents/source_client_mock.go +++ b/pkg/client/cloudevents/source_client_mock.go @@ -2,13 +2,16 @@ package cloudevents import ( "context" - "encoding/json" "fmt" "github.com/bwmarrin/snowflake" + cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/openshift-online/maestro/pkg/api" "github.com/openshift-online/maestro/pkg/services" cegeneric "open-cluster-management.io/sdk-go/pkg/cloudevents/generic" + "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/types" + "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" + workpayload "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -27,6 +30,7 @@ func init() { // SourceClientMock is a mock implementation of the SourceClient interface type SourceClientMock struct { + agent string resources api.ResourceList ResourceService services.ResourceService } @@ -35,6 +39,7 @@ var _ SourceClient = &SourceClientMock{} func NewSourceClientMock(resourceService services.ResourceService) SourceClient { return &SourceClientMock{ + agent: "mock-agent", ResourceService: resourceService, } } @@ -45,29 +50,38 @@ func (s *SourceClientMock) OnCreate(ctx context.Context, id string) error { return fmt.Errorf("failed to get resource: %v", serviceErr) } - resourceStatus := &api.ResourceStatus{ - ReconcileStatus: &api.ReconcileStatus{ - ObservedVersion: resource.Version, - SequenceID: sequenceGenerator.Generate().String(), - Conditions: []metav1.Condition{ - { - Type: "Applied", - Status: "True", - LastTransitionTime: metav1.Now(), - }, + eventType := types.CloudEventsType{ + CloudEventsDataType: workpayload.ManifestBundleEventDataType, + SubResource: types.SubResourceStatus, + Action: types.EventAction("update_request"), + } + evt := types.NewEventBuilder(s.agent, eventType). + WithResourceID(resource.ID). + WithStatusUpdateSequenceID(sequenceGenerator.Generate().String()). + WithResourceVersion(int64(resource.Version)). + WithClusterName(resource.ConsumerName). + WithOriginalSource("maestro"). + NewEvent() + + manifestBundleStatus := &payload.ManifestBundleStatus{ + Conditions: []metav1.Condition{ + { + Type: "Applied", + Status: "True", + LastTransitionTime: metav1.Now(), }, }, } - - resourceStatusJSON, err := json.Marshal(resourceStatus) - if err != nil { - return fmt.Errorf("failed to marshal resource status: %v", err) + if err := evt.SetData(cloudevents.ApplicationJSON, manifestBundleStatus); err != nil { + return fmt.Errorf("failed to encode manifestwork status to a cloudevent: %v", err) } - err = json.Unmarshal(resourceStatusJSON, &resource.Status) + + status, err := api.CloudEventToJSONMap(&evt) if err != nil { - return fmt.Errorf("failed to unmarshal resource status: %v", err) + return fmt.Errorf("failed to convert resource status cloudevent to jsonmap: %v", err) } + resource.Status = status newResource, _, serviceErr := s.ResourceService.UpdateStatus(ctx, resource) if serviceErr != nil { return fmt.Errorf("failed to update resource status: %v", serviceErr) @@ -87,42 +101,42 @@ func (s *SourceClientMock) OnUpdate(ctx context.Context, id string) error { found := false for i, r := range s.resources { if r.ID == resource.ID { - resourceStatusJSON, err := json.Marshal(resource.Status) + evt, err := api.JSONMAPToCloudEvent(resource.Status) if err != nil { - return fmt.Errorf("failed to marshal resource status: %v", err) + return fmt.Errorf("failed to convert resource status to cloudevent: %v", err) } - resourceStatus := &api.ResourceStatus{} - if err := json.Unmarshal(resourceStatusJSON, resourceStatus); err != nil { - return fmt.Errorf("failed to unmarshal resource status: %v", err) - } - if resourceStatus.ReconcileStatus == nil { - resourceStatus.ReconcileStatus = &api.ReconcileStatus{} + + manifestBundleStatus := &workpayload.ManifestBundleStatus{} + if err := evt.DataAs(manifestBundleStatus); err != nil { + return fmt.Errorf("failed to decode cloudevent payload as resource status: %v", err) } - resourceStatus.ReconcileStatus.ObservedVersion = resource.Version - resourceStatus.ReconcileStatus.SequenceID = sequenceGenerator.Generate().String() + condition := metav1.Condition{ Type: "Updated", Status: "True", LastTransitionTime: metav1.Now(), } - if len(resourceStatus.ReconcileStatus.Conditions) == 0 { - resourceStatus.ReconcileStatus.Conditions = []metav1.Condition{condition} + if len(manifestBundleStatus.Conditions) == 0 { + manifestBundleStatus.Conditions = []metav1.Condition{condition} + } else { + manifestBundleStatus.Conditions = append(manifestBundleStatus.Conditions, condition) } - resourceStatus.ReconcileStatus.Conditions = append(resourceStatus.ReconcileStatus.Conditions, condition) - resourceStatusJSON, err = json.Marshal(resourceStatus) - if err != nil { - return fmt.Errorf("failed to marshal resource status: %v", err) + if err := evt.SetData(cloudevents.ApplicationJSON, manifestBundleStatus); err != nil { + return fmt.Errorf("failed to encode manifestwork status to a cloudevent: %v", err) } - err = json.Unmarshal(resourceStatusJSON, &resource.Status) + + status, err := api.CloudEventToJSONMap(evt) if err != nil { - return fmt.Errorf("failed to unmarshal resource status: %v", err) + return fmt.Errorf("failed to convert resource status cloudevent to jsonmap: %v", err) } + resource.Status = status newResource, _, serviceErr := s.ResourceService.UpdateStatus(ctx, resource) if serviceErr != nil { return fmt.Errorf("failed to update resource status: %v", serviceErr) } + s.resources[i] = newResource found = true break diff --git a/pkg/handlers/resource.go b/pkg/handlers/resource.go index 2e5735e3..d95e2c45 100755 --- a/pkg/handlers/resource.go +++ b/pkg/handlers/resource.go @@ -1,7 +1,6 @@ package handlers import ( - "fmt" "net/http" "github.com/gorilla/mux" @@ -28,87 +27,34 @@ func NewResourceHandler(resource services.ResourceService, generic services.Gene } func (h resourceHandler) Create(w http.ResponseWriter, r *http.Request) { - var rs openapi.Resource - cfg := &handlerConfig{ - &rs, - []validate{ - validateEmpty(&rs, "Id", "id"), - validateNotEmpty(&rs, "ConsumerName", "consumer_name"), - validateNotEmpty(&rs, "Manifest", "manifest"), - validateNotEmpty(&rs, "GroupResource", "group_resource"), - validateDeleteOptionAndUpdateStrategy(&rs), - }, - func() (interface{}, *errors.ServiceError) { - ctx := r.Context() - resource, err := presenters.ConvertResource(rs) - if err != nil { - return nil, errors.GeneralError("failed to convert resource: %s", err) - } - resource, serviceErr := h.resource.Create(ctx, resource) - if serviceErr != nil { - return nil, serviceErr - } - res, err := presenters.PresentResource(resource) - if err != nil { - return nil, errors.GeneralError("failed to present resource: %s", err) - } - return res, nil - }, - handleError, - } - - handle(w, r, cfg, http.StatusCreated) + // not implemented + http.Error(w, "Not Implemented Yet", http.StatusNotImplemented) } func (h resourceHandler) Patch(w http.ResponseWriter, r *http.Request) { - var patch openapi.ResourcePatchRequest + // not implemented + http.Error(w, "Not Implemented Yet", http.StatusNotImplemented) +} +func (h resourceHandler) Get(w http.ResponseWriter, r *http.Request) { cfg := &handlerConfig{ - &patch, - []validate{ - validateNotEmpty(&patch, "Version", "version"), - validateNotEmpty(&patch, "Manifest", "manifest"), - }, - func() (interface{}, *errors.ServiceError) { - ctx := r.Context() + Action: func() (interface{}, *errors.ServiceError) { id := mux.Vars(r)["id"] - found, serviceErr := h.resource.Get(ctx, id) - if serviceErr != nil { - return nil, serviceErr - } - _, groupResource, deleteOption, updateStrategy, err := api.DecodeManifest(found.Payload) - if err != nil { - return nil, errors.GeneralError("failed to decode existing manifest: %s", err) - } - if patch.DeleteOption != nil { - deleteOption = patch.DeleteOption - } - if patch.UpdateStrategy != nil { - updateStrategy = patch.UpdateStrategy - } - payload, err := presenters.ConvertResourceManifest(patch.Manifest, groupResource, deleteOption, updateStrategy) - if err != nil { - return nil, errors.GeneralError("failed to convert resource manifest: %s", err) - } - resource, serviceErr := h.resource.Update(ctx, &api.Resource{ - Meta: api.Meta{ID: id}, - Version: *patch.Version, - Type: api.ResourceTypeSingle, - Payload: payload, - }) + ctx := r.Context() + resource, serviceErr := h.resource.Get(ctx, id) if serviceErr != nil { return nil, serviceErr } + res, err := presenters.PresentResource(resource) if err != nil { return nil, errors.GeneralError("failed to present resource: %s", err) } return res, nil }, - handleError, } - handle(w, r, cfg, http.StatusOK) + handleGet(w, r, cfg) } func (h resourceHandler) List(w http.ResponseWriter, r *http.Request) { @@ -117,18 +63,13 @@ func (h resourceHandler) List(w http.ResponseWriter, r *http.Request) { ctx := r.Context() listArgs := services.NewListArguments(r.URL.Query()) - if listArgs.Search == "" { - listArgs.Search = fmt.Sprintf("type='%s'", api.ResourceTypeSingle) - } else { - listArgs.Search = fmt.Sprintf("%s and type='%s'", listArgs.Search, api.ResourceTypeSingle) - } var resources []api.Resource - paging, serviceErr := h.generic.List(ctx, "username", listArgs, &resources) + paging, serviceErr := h.resource.ListWithArgs(ctx, "username", listArgs, &resources) if serviceErr != nil { return nil, serviceErr } resourceList := openapi.ResourceList{ - Kind: *presenters.ObjectKind(resources), + Kind: "ResourceList", Page: int32(paging.Page), Size: int32(paging.Size), Total: int32(paging.Total), @@ -156,27 +97,6 @@ func (h resourceHandler) List(w http.ResponseWriter, r *http.Request) { handleList(w, r, cfg) } -func (h resourceHandler) Get(w http.ResponseWriter, r *http.Request) { - cfg := &handlerConfig{ - Action: func() (interface{}, *errors.ServiceError) { - id := mux.Vars(r)["id"] - ctx := r.Context() - resource, serviceErr := h.resource.Get(ctx, id) - if serviceErr != nil { - return nil, serviceErr - } - - res, err := presenters.PresentResource(resource) - if err != nil { - return nil, errors.GeneralError("failed to present resource: %s", err) - } - return res, nil - }, - } - - handleGet(w, r, cfg) -} - // Resource Deletion Flow: // 1. User requests deletion // 2. Maestro marks resource as deleting, adds delete event to DB @@ -197,69 +117,3 @@ func (h resourceHandler) Delete(w http.ResponseWriter, r *http.Request) { } handleDelete(w, r, cfg, http.StatusNoContent) } - -func (h resourceHandler) GetBundle(w http.ResponseWriter, r *http.Request) { - cfg := &handlerConfig{ - Action: func() (interface{}, *errors.ServiceError) { - id := mux.Vars(r)["id"] - ctx := r.Context() - resource, serviceErr := h.resource.Get(ctx, id) - if serviceErr != nil { - return nil, serviceErr - } - - resBundle, err := presenters.PresentResourceBundle(resource) - if err != nil { - return nil, errors.GeneralError("failed to present resource bundle: %s", err) - } - return resBundle, nil - }, - } - - handleGet(w, r, cfg) -} - -func (h resourceHandler) ListBundle(w http.ResponseWriter, r *http.Request) { - cfg := &handlerConfig{ - Action: func() (interface{}, *errors.ServiceError) { - ctx := r.Context() - - listArgs := services.NewListArguments(r.URL.Query()) - if listArgs.Search == "" { - listArgs.Search = fmt.Sprintf("type='%s'", api.ResourceTypeBundle) - } else { - listArgs.Search = fmt.Sprintf("%s and type='%s'", listArgs.Search, api.ResourceTypeBundle) - } - var resources []api.Resource - paging, serviceErr := h.resource.ListWithArgs(ctx, "username", listArgs, &resources) - if serviceErr != nil { - return nil, serviceErr - } - resourceBundleList := openapi.ResourceBundleList{ - Kind: "ResourceBundleList", - Page: int32(paging.Page), - Size: int32(paging.Size), - Total: int32(paging.Total), - Items: []openapi.ResourceBundle{}, - } - - for _, resource := range resources { - converted, err := presenters.PresentResourceBundle(&resource) - if err != nil { - return nil, errors.GeneralError("failed to present resource: %s", err) - } - resourceBundleList.Items = append(resourceBundleList.Items, *converted) - } - if listArgs.Fields != nil { - filteredItems, err := presenters.SliceFilter(listArgs.Fields, resourceBundleList.Items) - if err != nil { - return nil, err - } - return filteredItems, nil - } - return resourceBundleList, nil - }, - } - - handleList(w, r, cfg) -} diff --git a/pkg/handlers/resource_bundle.go b/pkg/handlers/resource_bundle.go new file mode 100644 index 00000000..77c34b08 --- /dev/null +++ b/pkg/handlers/resource_bundle.go @@ -0,0 +1,103 @@ +package handlers + +import ( + "net/http" + + "github.com/gorilla/mux" + + "github.com/openshift-online/maestro/pkg/api" + "github.com/openshift-online/maestro/pkg/api/openapi" + "github.com/openshift-online/maestro/pkg/api/presenters" + "github.com/openshift-online/maestro/pkg/errors" + "github.com/openshift-online/maestro/pkg/services" +) + +var _ RestHandler = resourceBundleHandler{} + +type resourceBundleHandler struct { + resource services.ResourceService + generic services.GenericService +} + +func NewResourceBundleHandler(resource services.ResourceService, generic services.GenericService) *resourceBundleHandler { + return &resourceBundleHandler{ + resource: resource, + generic: generic, + } +} + +func (h resourceBundleHandler) Create(w http.ResponseWriter, r *http.Request) { + // not implemented + http.Error(w, "Not Implemented Yet", http.StatusNotImplemented) +} + +func (h resourceBundleHandler) Patch(w http.ResponseWriter, r *http.Request) { + // not implemented + http.Error(w, "Not Implemented Yet", http.StatusNotImplemented) +} + +func (h resourceBundleHandler) Get(w http.ResponseWriter, r *http.Request) { + cfg := &handlerConfig{ + Action: func() (interface{}, *errors.ServiceError) { + id := mux.Vars(r)["id"] + ctx := r.Context() + resource, serviceErr := h.resource.Get(ctx, id) + if serviceErr != nil { + return nil, serviceErr + } + + rb, err := presenters.PresentResourceBundle(resource) + if err != nil { + return nil, errors.GeneralError("failed to present resource bundle: %s", err) + } + return rb, nil + }, + } + + handleGet(w, r, cfg) +} + +func (h resourceBundleHandler) List(w http.ResponseWriter, r *http.Request) { + cfg := &handlerConfig{ + Action: func() (interface{}, *errors.ServiceError) { + ctx := r.Context() + + listArgs := services.NewListArguments(r.URL.Query()) + var resources []api.Resource + paging, serviceErr := h.resource.ListWithArgs(ctx, "username", listArgs, &resources) + if serviceErr != nil { + return nil, serviceErr + } + resourceBundleList := openapi.ResourceBundleList{ + Kind: "ResourceBundleList", + Page: int32(paging.Page), + Size: int32(paging.Size), + Total: int32(paging.Total), + Items: []openapi.ResourceBundle{}, + } + + for _, resource := range resources { + converted, err := presenters.PresentResourceBundle(&resource) + if err != nil { + return nil, errors.GeneralError("failed to present resource bundle: %s", err) + } + resourceBundleList.Items = append(resourceBundleList.Items, *converted) + } + if listArgs.Fields != nil { + filteredItems, err := presenters.SliceFilter(listArgs.Fields, resourceBundleList.Items) + if err != nil { + return nil, err + } + return filteredItems, nil + } + return resourceBundleList, nil + }, + } + + handleList(w, r, cfg) +} + +func (h resourceBundleHandler) Delete(w http.ResponseWriter, r *http.Request) { + // not implemented + http.Error(w, "Not Implemented Yet", http.StatusNotImplemented) +} diff --git a/pkg/handlers/validation.go b/pkg/handlers/validation.go index 4367c525..48f6c6d6 100755 --- a/pkg/handlers/validation.go +++ b/pkg/handlers/validation.go @@ -3,7 +3,6 @@ package handlers import ( "reflect" - "github.com/openshift-online/maestro/pkg/api/openapi" "github.com/openshift-online/maestro/pkg/errors" ) @@ -38,24 +37,3 @@ func validateEmpty(i interface{}, fieldName string, field string) validate { return nil } } - -// validateDeleteOptionAndUpdateStrategy validates the delete option and update strategy -// for a resource, to ensure that update strategy ReadOnly is only allowed with delete option Orphan. -func validateDeleteOptionAndUpdateStrategy(rs *openapi.Resource) validate { - return func() *errors.ServiceError { - if rs.DeleteOption != nil && rs.UpdateStrategy != nil { - deleteType, ok := rs.DeleteOption["propagationPolicy"].(string) - if !ok { - return errors.Validation("invalid delete option") - } - updateStrategy, ok := rs.UpdateStrategy["type"].(string) - if !ok { - return errors.Validation("invalid update strategy") - } - if deleteType != "Orphan" && updateStrategy == "ReadOnly" { - return errors.Validation("update strategy ReadOnly is only allowed with delete option Orphan") - } - } - return nil - } -} diff --git a/pkg/services/resource.go b/pkg/services/resource.go index 40e2ee8e..41566c6a 100755 --- a/pkg/services/resource.go +++ b/pkg/services/resource.go @@ -75,7 +75,7 @@ func (s *sqlResourceService) Create(ctx context.Context, resource *api.Resource) return nil, errors.Validation("the name in the resource is invalid, %v", err) } } - if err := ValidateManifest(resource.Type, resource.Payload); err != nil { + if err := ValidateManifest(resource.Payload); err != nil { return nil, errors.Validation("the manifest in the resource is invalid, %v", err) } @@ -126,7 +126,7 @@ func (s *sqlResourceService) Update(ctx context.Context, resource *api.Resource) return found, nil } - if err := ValidateManifestUpdate(resource.Type, resource.Payload, found.Payload); err != nil { + if err := ValidateManifestUpdate(resource.Payload, found.Payload); err != nil { return nil, errors.Validation("the new manifest in the resource is invalid, %v", err) } diff --git a/pkg/services/resource_test.go b/pkg/services/resource_test.go index 5ca29375..7731d091 100755 --- a/pkg/services/resource_test.go +++ b/pkg/services/resource_test.go @@ -28,13 +28,12 @@ func TestResourceFindByConsumerID(t *testing.T) { resourceService := NewResourceService(dbmocks.NewMockAdvisoryLockFactory(), resourceDAO, events, nil) resources := api.ResourceList{ - &api.Resource{ConsumerName: Fukuisaurus, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, - &api.Resource{ConsumerName: Fukuisaurus, Type: api.ResourceTypeBundle, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, - &api.Resource{ConsumerName: Fukuisaurus, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, - &api.Resource{ConsumerName: Seismosaurus, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, - &api.Resource{ConsumerName: Seismosaurus, Type: api.ResourceTypeBundle, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"e3eb7db1-b124-4a4d-8bb6-cc779c01b402\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, - &api.Resource{ConsumerName: Breviceratops, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, - &api.Resource{ConsumerName: Breviceratops, Type: api.ResourceTypeBundle, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Fukuisaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Fukuisaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Fukuisaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Seismosaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"e3eb7db1-b124-4a4d-8bb6-cc779c01b402\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Seismosaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"e3eb7db1-b124-4a4d-8bb6-cc779c01b402\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Breviceratops, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, } for _, resource := range resources { _, err := resourceService.Create(context.Background(), resource) @@ -50,7 +49,7 @@ func TestResourceFindByConsumerID(t *testing.T) { breviceratops, err := resourceDAO.FindByConsumerName(context.Background(), Breviceratops) gm.Expect(err).To(gm.BeNil()) - gm.Expect(len(breviceratops)).To(gm.Equal(2)) + gm.Expect(len(breviceratops)).To(gm.Equal(1)) } func TestCreateInvalidResource(t *testing.T) { @@ -78,32 +77,32 @@ func TestResourceList(t *testing.T) { resourceService := NewResourceService(dbmocks.NewMockAdvisoryLockFactory(), resourceDAO, events, nil) resources := api.ResourceList{ - &api.Resource{ConsumerName: Fukuisaurus, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, - &api.Resource{ConsumerName: Fukuisaurus, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, - &api.Resource{ConsumerName: Fukuisaurus, Type: api.ResourceTypeBundle, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, - &api.Resource{ConsumerName: Seismosaurus, Type: api.ResourceTypeSingle, Payload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-03-07T03:29:03.194843266Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"test\",\"group\":\"\",\"resource\":\"configmaps\",\"namespace\":\"test\"}}]}}")}, + &api.Resource{ConsumerName: Fukuisaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Fukuisaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, + &api.Resource{ConsumerName: Seismosaurus, Payload: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}")}, } for _, resource := range resources { _, err := resourceService.Create(context.Background(), resource) gm.Expect(err).To(gm.BeNil()) } - resoruces, err := resourceService.List(types.ListOptions{ - ClusterName: Fukuisaurus, + resources, err := resourceService.List(types.ListOptions{ + ClusterName: Fukuisaurus, + CloudEventsDataType: payload.ManifestEventDataType, }) gm.Expect(err).To(gm.BeNil()) - gm.Expect(len(resoruces)).To(gm.Equal(3)) + gm.Expect(len(resources)).To(gm.Equal(2)) - resoruces, err = resourceService.List(types.ListOptions{ + resources, err = resourceService.List(types.ListOptions{ ClusterName: Seismosaurus, }) gm.Expect(err).To(gm.BeNil()) - gm.Expect(len(resoruces)).To(gm.Equal(1)) + gm.Expect(len(resources)).To(gm.Equal(1)) - resoruces, err = resourceService.List(types.ListOptions{ + resources, err = resourceService.List(types.ListOptions{ ClusterName: Seismosaurus, - CloudEventsDataType: payload.ManifestEventDataType, + CloudEventsDataType: payload.ManifestBundleEventDataType, }) gm.Expect(err).To(gm.BeNil()) - gm.Expect(len(resoruces)).To(gm.Equal(1)) + gm.Expect(len(resources)).To(gm.Equal(1)) } diff --git a/pkg/services/validation.go b/pkg/services/validation.go index cb6e4882..a32896e2 100644 --- a/pkg/services/validation.go +++ b/pkg/services/validation.go @@ -40,30 +40,18 @@ func ValidateConsumer(consumer *api.Consumer) error { return fmt.Errorf(errs.ToAggregate().Error()) } -func ValidateManifest(resType api.ResourceType, manifest datatypes.JSONMap) error { - switch resType { - case api.ResourceTypeSingle: - // TODO: validate the deleteOption and updateStrategy - obj, _, _, _, err := api.DecodeManifest(manifest) - if err != nil { - return fmt.Errorf("failed to decode manifest: %v", err) - } - return ValidateObject(obj) - case api.ResourceTypeBundle: - manifestBundle, err := api.DecodeManifestBundle(manifest) - if err != nil { - return fmt.Errorf("failed to decode manifest bundle: %v", err) - } - if manifestBundle == nil { - return fmt.Errorf("manifest bundle is empty") - } - for _, obj := range manifestBundle.Manifests { - if err := ValidateObject(obj); err != nil { - return err - } +func ValidateManifest(manifest datatypes.JSONMap) error { + manifestWrapper, err := api.DecodeManifest(manifest) + if err != nil { + return fmt.Errorf("failed to decode manifest: %v", err) + } + if manifestWrapper == nil { + return fmt.Errorf("manifest is empty") + } + for _, obj := range manifestWrapper.Manifests { + if err := ValidateObject(obj); err != nil { + return err } - default: - return fmt.Errorf("unknown resource type: %s", resType) } return nil @@ -99,43 +87,28 @@ func ValidateObject(obj datatypes.JSONMap) error { return fmt.Errorf(errs.ToAggregate().Error()) } -func ValidateManifestUpdate(resType api.ResourceType, new, old datatypes.JSONMap) error { - switch resType { - case api.ResourceTypeSingle: - newObj, _, _, _, err := api.DecodeManifest(new) - if err != nil { - return fmt.Errorf("failed to decode new manifest: %v", err) - } - oldObj, _, _, _, err := api.DecodeManifest(old) - if err != nil { - return fmt.Errorf("failed to decode old manifest: %v", err) - } - return ValidateObjectUpdate(newObj, oldObj) - case api.ResourceTypeBundle: - newManifestBundle, err := api.DecodeManifestBundle(new) - if err != nil { - return fmt.Errorf("failed to decode new manifest bundle: %v", err) - } - if newManifestBundle == nil { - return fmt.Errorf("new manifest bundle is empty") - } - oldManifestBundle, err := api.DecodeManifestBundle(old) - if err != nil { - return fmt.Errorf("failed to decode old manifest bundle: %v", err) - } - if oldManifestBundle == nil { - return fmt.Errorf("old manifest bundle is empty") - } - if len(newManifestBundle.Manifests) != len(oldManifestBundle.Manifests) { - return fmt.Errorf("new and old manifest bundles have different number of objects") - } - for i := range newManifestBundle.Manifests { - if err := ValidateObjectUpdate(newManifestBundle.Manifests[i], oldManifestBundle.Manifests[i]); err != nil { - return err - } +func ValidateManifestUpdate(new, old datatypes.JSONMap) error { + newManifestWrapper, err := api.DecodeManifest(new) + if err != nil { + return fmt.Errorf("failed to decode new manifest: %v", err) + } + if newManifestWrapper == nil { + return fmt.Errorf("new manifest is empty") + } + oldManifestWrapper, err := api.DecodeManifest(old) + if err != nil { + return fmt.Errorf("failed to decode old manifest: %v", err) + } + if oldManifestWrapper == nil { + return fmt.Errorf("old manifest is empty") + } + if len(newManifestWrapper.Manifests) != len(oldManifestWrapper.Manifests) { + return fmt.Errorf("new and old manifest have different number of objects") + } + for i := range newManifestWrapper.Manifests { + if err := ValidateObjectUpdate(newManifestWrapper.Manifests[i], oldManifestWrapper.Manifests[i]); err != nil { + return err } - default: - return fmt.Errorf("unknown resource type: %s", resType) } return nil diff --git a/pkg/services/validation_test.go b/pkg/services/validation_test.go index 2624cd4d..7020c1f1 100644 --- a/pkg/services/validation_test.go +++ b/pkg/services/validation_test.go @@ -89,43 +89,23 @@ func TestValidateResourceName(t *testing.T) { func TestValidateNewManifest(t *testing.T) { cases := []struct { name string - resType api.ResourceType manifest datatypes.JSONMap expectedErrorMsg string }{ { - name: "validated single manifest", - resType: api.ResourceTypeSingle, - manifest: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), - }, - { - name: "validated bundle manifest", - resType: api.ResourceTypeBundle, + name: "validated manifest", manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), }, { - name: "invalidated single manifest", - resType: api.ResourceTypeSingle, - manifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), - expectedErrorMsg: "failed to decode manifest: invalid number of manifests in the event payload: 2", - }, - { - name: "invalidated bundle manifest", - resType: api.ResourceTypeBundle, + name: "invalidated manifest", manifest: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"), - expectedErrorMsg: "manifest bundle is empty", - }, - { - name: "invalidated resource type", - resType: "invalid", - manifest: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"), - expectedErrorMsg: "unknown resource type: invalid", + expectedErrorMsg: "manifest is empty", }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { - err := ValidateManifest(c.resType, c.manifest) + err := ValidateManifest(c.manifest) if err != nil && err.Error() != c.expectedErrorMsg { t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) } @@ -208,49 +188,26 @@ func TestValidateNewObject(t *testing.T) { func TestValidateUpdateManifest(t *testing.T) { cases := []struct { name string - resType api.ResourceType newPayload datatypes.JSONMap oldManifest datatypes.JSONMap expectedErrorMsg string }{ { - name: "validated single manifest", - resType: api.ResourceTypeSingle, - newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), - oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), - }, - { - name: "validated bundle manifest", - resType: api.ResourceTypeBundle, + name: "validated manifest", newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"grpc\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"}},{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), }, { - name: "invalidated single manifest", - resType: api.ResourceTypeSingle, - newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"resourceid\":\"c4df9ff0-bfeb-5bc6-a0ab-4c9128d698b4\",\"clustername\":\"b288a9da-8bfe-4c82-94cc-2b48e773fc46\",\"resourceversion\":1,\"data\":{\"manifests\":[{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"nginx\"}},\"template\":{\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginxinc/nginx-unprivileged\"}]},\"metadata\":{\"labels\":{\"app\":\"nginx\"}}}}}],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), - oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifests\":[],\"deleteOption\":{\"propagationPolicy\":\"Foreground\"},\"manifestConfigs\":[{\"updateStrategy\":{\"type\":\"ServerSideApply\"},\"resourceIdentifier\":{\"name\":\"nginx\",\"group\":\"apps\",\"resource\":\"deployments\",\"namespace\":\"default\"}}]}}"), - expectedErrorMsg: "failed to decode old manifest: invalid number of manifests in the event payload: 0", - }, - { - name: "invalidated bundle manifest", - resType: api.ResourceTypeBundle, - newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"), - oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"), - expectedErrorMsg: "new or old manifest bundle is empty", - }, - { - name: "invalidated resource type", - resType: "invalid", + name: "invalidated manifest", newPayload: newPayload(t, "{\"id\":\"75479c10-b537-4261-8058-ca2e36bac384\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"), oldManifest: newPayload(t, "{\"id\":\"266a8cd2-2fab-4e89-9bf0-a56425ebcdf8\",\"time\":\"2024-02-05T17:31:05Z\",\"type\":\"io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request\",\"source\":\"maestro\",\"specversion\":\"1.0\",\"datacontenttype\":\"application/json\",\"data\":{\"manifest\":{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"test\",\"namespace\":\"test\"}}}}"), - expectedErrorMsg: "unknown resource type: invalid", + expectedErrorMsg: "new or old manifest is empty", }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { - err := ValidateManifestUpdate(c.resType, c.newPayload, c.oldManifest) + err := ValidateManifestUpdate(c.newPayload, c.oldManifest) if err != nil && err.Error() != c.expectedErrorMsg { t.Errorf("expected %#v but got: %#v", c.expectedErrorMsg, err) } diff --git a/test/e2e/pkg/consumer_test.go b/test/e2e/pkg/consumer_test.go index 536184b8..46f1214a 100644 --- a/test/e2e/pkg/consumer_test.go +++ b/test/e2e/pkg/consumer_test.go @@ -15,7 +15,6 @@ var _ = Describe("Consumers", Ordered, Label("e2e-tests-consumers"), func() { Context("Consumer CRUD Tests", func() { consumerA := openapi.Consumer{Name: openapi.PtrString(fmt.Sprintf("consumer-a-%s", rand.String(5)))} consumerB := openapi.Consumer{Name: openapi.PtrString(fmt.Sprintf("consumer-b-%s", rand.String(5)))} - resource := helper.NewAPIResource(*consumerB.Name, fmt.Sprintf("nginx-%s", rand.String(5)), 1) AfterAll(func() { // delete the consumer @@ -27,24 +26,18 @@ var _ = Describe("Consumers", Ordered, Label("e2e-tests-consumers"), func() { Expect(err.Error()).To(ContainSubstring("Not Found")) Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) - // delete the consumer associated with resource + // delete the consumer resp, err = apiClient.DefaultApi.ApiMaestroV1ConsumersIdDelete(ctx, *consumerB.Id).Execute() - Expect(err).To(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusForbidden)) // 403 forbid deletion - - // delete the resource on the consumer - resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() - Expect(err).To(Succeed()) + Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - // only if permanently delete the resource, the consumer can be deleted - resp, err = apiClient.DefaultApi.ApiMaestroV1ConsumersIdDelete(ctx, *consumerB.Id).Execute() - Expect(err).To(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusForbidden)) // 403 forbid deletion + _, resp, err = apiClient.DefaultApi.ApiMaestroV1ConsumersIdGet(ctx, *consumerB.Id).Execute() + Expect(err.Error()).To(ContainSubstring("Not Found")) + Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) }) It("create consumer", func() { - // create a consumer without resource + // create a consumer created, resp, err := apiClient.DefaultApi.ApiMaestroV1ConsumersPost(ctx).Consumer(consumerA).Execute() Expect(err).To(Succeed()) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) @@ -56,36 +49,37 @@ var _ = Describe("Consumers", Ordered, Label("e2e-tests-consumers"), func() { Expect(resp.StatusCode).To(Equal(http.StatusOK)) Expect(got).NotTo(BeNil()) - // create a consumer with resource + // create a consumer created, resp, err = apiClient.DefaultApi.ApiMaestroV1ConsumersPost(ctx).Consumer(consumerB).Execute() Expect(err).To(Succeed()) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) Expect(*created.Id).NotTo(BeEmpty()) consumerB = *created - res, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(resource).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*res.Id).ShouldNot(BeEmpty()) - Expect(*res.Version).To(Equal(int32(1))) - resource = *res + got, resp, err = apiClient.DefaultApi.ApiMaestroV1ConsumersIdGet(ctx, *consumerB.Id).Execute() + Expect(err).To(Succeed()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(got).NotTo(BeNil()) }) - It("list consumer", func() { + It("list consumers", func() { consumerList, resp, err := apiClient.DefaultApi.ApiMaestroV1ConsumersGet(ctx).Execute() Expect(err).To(Succeed()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) Expect(consumerList).NotTo(BeNil()) - Expect(len(consumerList.Items) > 0).To(BeTrue()) - fmt.Printf("consumer list: %v\n", consumerList.Items) + Expect(len(consumerList.Items) >= 2).To(BeTrue()) - got := false + gotA, gotB := false, false for _, c := range consumerList.Items { if *c.Name == *consumerA.Name { - got = true + gotA = true + } + if *c.Name == *consumerB.Name { + gotB = true } } - Expect(got).To(BeTrue()) + Expect(gotA).To(BeTrue()) + Expect(gotB).To(BeTrue()) }) It("patch consumer", func() { diff --git a/test/e2e/pkg/grpc_test.go b/test/e2e/pkg/grpc_test.go index faa1e80a..04c2b5ad 100644 --- a/test/e2e/pkg/grpc_test.go +++ b/test/e2e/pkg/grpc_test.go @@ -26,11 +26,11 @@ import ( ) var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { - Context("GRPC Manifest Tests", func() { + Context("GRPC API Tests", func() { deployName := fmt.Sprintf("nginx-%s", rand.String(5)) resourceID := uuid.NewString() resourceStatus := &api.ResourceStatus{ - ReconcileStatus: &api.ReconcileStatus{}, + ManifestBundleStatus: &payload.ManifestBundleStatus{}, } It("subscribe to resource status with grpc client", func() { @@ -67,269 +67,44 @@ var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { if err != nil { continue } - resourceStatus.ReconcileStatus.ObservedVersion = resourceVersion + resourceStatus.ObservedVersion = resourceVersion - manifestBundleStatus := &payload.ManifestBundleStatus{} - if err := evt.DataAs(manifestBundleStatus); err != nil { + if err := evt.DataAs(resourceStatus.ManifestBundleStatus); err != nil { continue } - - if len(manifestBundleStatus.ResourceStatus) != 1 { - return - } - resourceStatus.ReconcileStatus.Conditions = manifestBundleStatus.ResourceStatus[0].Conditions - if meta.IsStatusConditionTrue(manifestBundleStatus.Conditions, common.ManifestsDeleted) { - deletedCondition := meta.FindStatusCondition(manifestBundleStatus.Conditions, common.ManifestsDeleted) - resourceStatus.ReconcileStatus.Conditions = append(resourceStatus.ReconcileStatus.Conditions, *deletedCondition) - } - for _, value := range manifestBundleStatus.ResourceStatus[0].StatusFeedbacks.Values { - if value.Name == "status" { - contentStatus := make(map[string]interface{}) - if err := json.Unmarshal([]byte(*value.Value.JsonRaw), &contentStatus); err != nil { - continue - } - resourceStatus.ContentStatus = contentStatus - } - } } }() }) - It("publish a resource spec using grpc client", func() { - evt := helper.NewEvent(sourceID, "create_request", agentTestOpts.consumerName, resourceID, deployName, 1, 1) - pbEvt := &pbv1.CloudEvent{} - err := grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) - Expect(err).To(BeNil(), "failed to convert spec from cloudevent to protobuf") - _, err = grpcClient.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}) + It("publish a resource spec with grpc client", func() { + evt, err := helper.NewEvent(sourceID, "create_request", agentTestOpts.consumerName, resourceID, deployName, 1, 1) Expect(err).ShouldNot(HaveOccurred()) - }) - - It("Subscribe to the resource status using grpc client", func() { - Eventually(func() error { - if resourceStatus.ReconcileStatus == nil { - return fmt.Errorf("reconcile status is empty") - } - - if !meta.IsStatusConditionTrue(resourceStatus.ReconcileStatus.Conditions, "Applied") { - return fmt.Errorf("resource not applied") - } - - if !meta.IsStatusConditionTrue(resourceStatus.ReconcileStatus.Conditions, "Available") { - return fmt.Errorf("resource not Available") - } - - replicas, ok := resourceStatus.ContentStatus["replicas"] - if !ok { - return fmt.Errorf("replicas not found in content status") - } - - if replicas.(float64) != float64(1) { - return fmt.Errorf("unexpected replicas, expected 1, got %d", replicas) - } - - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("get the nginx deployment from cluster", func() { - Eventually(func() error { - deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - return err - } - if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) - } - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("get the resource with the maestro api", func() { - gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*gotResource.Id).To(Equal(resourceID)) - Expect(*gotResource.Version).To(Equal(int32(1))) - }) - - It("publish a resource spec with update request using grpc client", func() { - evt := helper.NewEvent(sourceID, "update_request", agentTestOpts.consumerName, resourceID, deployName, 1, 2) pbEvt := &pbv1.CloudEvent{} - err := grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) + err = grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) Expect(err).To(BeNil(), "failed to convert spec from cloudevent to protobuf") _, err = grpcClient.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}) Expect(err).ShouldNot(HaveOccurred()) }) - It("Subscribe to the resource status using grpc client", func() { + It("subscribe to the resource status with grpc client", func() { Eventually(func() error { - if resourceStatus.ReconcileStatus == nil { - return fmt.Errorf("reconcile status is empty") + if resourceStatus.ManifestBundleStatus == nil { + return fmt.Errorf("resource status is empty") } - if !meta.IsStatusConditionTrue(resourceStatus.ReconcileStatus.Conditions, "Applied") { + if !meta.IsStatusConditionTrue(resourceStatus.ManifestBundleStatus.Conditions, "Applied") { return fmt.Errorf("resource not applied") } - if !meta.IsStatusConditionTrue(resourceStatus.ReconcileStatus.Conditions, "Available") { + if !meta.IsStatusConditionTrue(resourceStatus.ManifestBundleStatus.Conditions, "Available") { return fmt.Errorf("resource not Available") } - replicas, ok := resourceStatus.ContentStatus["replicas"] - if !ok { - return fmt.Errorf("replicas not found in content status") - } - - if replicas.(float64) != float64(2) { - return fmt.Errorf("unexpected replicas, expected 2, got %d", replicas) - } - - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("get the nginx deployment from cluster", func() { - Eventually(func() error { - deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - return err - } - if *deploy.Spec.Replicas != 2 { - return fmt.Errorf("unexpected replicas, expected 2, got %d", *deploy.Spec.Replicas) - } - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("get the resource with the maestro api", func() { - gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*gotResource.Id).To(Equal(resourceID)) - Expect(*gotResource.Version).To(Equal(int32(2))) - }) - - It("publish a resource spec with delete request using grpc client", func() { - evt := helper.NewEvent(sourceID, "delete_request", agentTestOpts.consumerName, resourceID, deployName, 2, 2) - pbEvt := &pbv1.CloudEvent{} - err := grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) - Expect(err).To(BeNil(), "failed to convert spec from cloudevent to protobuf") - _, err = grpcClient.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}) - Expect(err).ShouldNot(HaveOccurred()) - }) - - It("Subscribe to the resource status using grpc client", func() { - Eventually(func() error { - if resourceStatus.ReconcileStatus == nil { - return fmt.Errorf("reconcile status is empty") - } - - if !meta.IsStatusConditionTrue(resourceStatus.ReconcileStatus.Conditions, common.ManifestsDeleted) { - return fmt.Errorf("resource not deleted") + if len(resourceStatus.ManifestBundleStatus.ResourceStatus) != 1 { + return fmt.Errorf("unexpected number of resource status, expected 1, got %d", len(resourceStatus.ManifestBundleStatus.ResourceStatus)) } - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("get the nginx deployment from cluster", func() { - Eventually(func() error { - _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - return fmt.Errorf("nginx deployment still exists") - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("get the resource with the maestro api", func() { - _, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() - Expect(err).To(HaveOccurred(), "Expected 404 error") - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) - }) - }) - - Context("GRPC Manifest Bundle Tests", func() { - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - resourceID := uuid.NewString() - resourceBundleStatus := &api.ResourceBundleStatus{ - ManifestBundleStatus: &payload.ManifestBundleStatus{}, - } - - It("subscribe to resource bundle status with grpc client", func() { - go func() { - subClient, err := grpcClient.Subscribe(ctx, &pbv1.SubscriptionRequest{Source: sourceID}) - if err != nil { - return - } - - for { - pvEvt, err := subClient.Recv() - if err == io.EOF { - return - } - if err != nil { - return - } - evt, err := binding.ToEvent(ctx, grpcprotocol.NewMessage(pvEvt)) - if err != nil { - continue - } - - evtExtensions := evt.Context.GetExtensions() - resID, err := cetypes.ToString(evtExtensions[types.ExtensionResourceID]) - if err != nil { - continue - } - - if resID != resourceID { - continue - } - - resourceVersion, err := cetypes.ToInteger(evtExtensions[types.ExtensionResourceVersion]) - if err != nil { - continue - } - resourceBundleStatus.ObservedVersion = resourceVersion - - if err := evt.DataAs(resourceBundleStatus.ManifestBundleStatus); err != nil { - continue - } - } - }() - }) - - It("publish a resource bundle spec using grpc client", func() { - evt := helper.NewEvent(sourceID, "create_request", agentTestOpts.consumerName, resourceID, deployName, 1, 1) - pbEvt := &pbv1.CloudEvent{} - err := grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) - Expect(err).To(BeNil(), "failed to convert spec from cloudevent to protobuf") - _, err = grpcClient.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}) - Expect(err).ShouldNot(HaveOccurred()) - }) - - It("Subscribe to the resource bundle status using grpc client", func() { - Eventually(func() error { - if resourceBundleStatus.ManifestBundleStatus == nil { - return fmt.Errorf("resource bundle status is empty") - } - - if !meta.IsStatusConditionTrue(resourceBundleStatus.ManifestBundleStatus.Conditions, "Applied") { - return fmt.Errorf("resource bundle not applied") - } - - if !meta.IsStatusConditionTrue(resourceBundleStatus.ManifestBundleStatus.Conditions, "Available") { - return fmt.Errorf("resource bundle not Available") - } - - if len(resourceBundleStatus.ManifestBundleStatus.ResourceStatus) != 1 { - return fmt.Errorf("unexpected number of resource status, expected 1, got %d", len(resourceBundleStatus.ManifestBundleStatus.ResourceStatus)) - } - - resourceStatus := resourceBundleStatus.ManifestBundleStatus.ResourceStatus[0] + resourceStatus := resourceStatus.ManifestBundleStatus.ResourceStatus[0] if len(resourceStatus.StatusFeedbacks.Values) != 1 { return fmt.Errorf("unexpected number of status feedbacks, expected 1, got %d", len(resourceStatus.StatusFeedbacks.Values)) } @@ -353,7 +128,7 @@ var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the nginx deployment from cluster", func() { + It("get the deployment from cluster", func() { Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) if err != nil { @@ -366,42 +141,43 @@ var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the resource bundle with the maestro api", func() { - gotResourceBundle, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, resourceID).Execute() + It("get the resource via maestro api", func() { + gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() Expect(err).ShouldNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*gotResourceBundle.Id).To(Equal(resourceID)) - Expect(*gotResourceBundle.Version).To(Equal(int32(1))) + Expect(*gotResource.Id).To(Equal(resourceID)) + Expect(*gotResource.Version).To(Equal(int32(1))) }) - It("publish a resource bundle spec with update request using grpc client", func() { - evt := helper.NewEvent(sourceID, "update_request", agentTestOpts.consumerName, resourceID, deployName, 1, 2) + It("publish a resource update with grpc client", func() { + evt, err := helper.NewEvent(sourceID, "update_request", agentTestOpts.consumerName, resourceID, deployName, 1, 2) + Expect(err).ShouldNot(HaveOccurred()) pbEvt := &pbv1.CloudEvent{} - err := grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) + err = grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) Expect(err).To(BeNil(), "failed to convert spec from cloudevent to protobuf") _, err = grpcClient.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}) Expect(err).ShouldNot(HaveOccurred()) }) - It("Subscribe to the resource bundle status using grpc client", func() { + It("subscribe to the resource status with grpc client", func() { Eventually(func() error { - if resourceBundleStatus.ManifestBundleStatus == nil { - return fmt.Errorf("resource bundle status is empty") + if resourceStatus.ManifestBundleStatus == nil { + return fmt.Errorf("resource status is empty") } - if !meta.IsStatusConditionTrue(resourceBundleStatus.ManifestBundleStatus.Conditions, "Applied") { - return fmt.Errorf("resource bundle not applied") + if !meta.IsStatusConditionTrue(resourceStatus.ManifestBundleStatus.Conditions, "Applied") { + return fmt.Errorf("resource not applied") } - if !meta.IsStatusConditionTrue(resourceBundleStatus.ManifestBundleStatus.Conditions, "Available") { - return fmt.Errorf("resource bundle not Available") + if !meta.IsStatusConditionTrue(resourceStatus.ManifestBundleStatus.Conditions, "Available") { + return fmt.Errorf("resource not Available") } - if len(resourceBundleStatus.ManifestBundleStatus.ResourceStatus) != 1 { - return fmt.Errorf("unexpected number of resource status, expected 1, got %d", len(resourceBundleStatus.ManifestBundleStatus.ResourceStatus)) + if len(resourceStatus.ManifestBundleStatus.ResourceStatus) != 1 { + return fmt.Errorf("unexpected number of resource status, expected 1, got %d", len(resourceStatus.ManifestBundleStatus.ResourceStatus)) } - resourceStatus := resourceBundleStatus.ManifestBundleStatus.ResourceStatus[0] + resourceStatus := resourceStatus.ManifestBundleStatus.ResourceStatus[0] if len(resourceStatus.StatusFeedbacks.Values) != 1 { return fmt.Errorf("unexpected number of status feedbacks, expected 1, got %d", len(resourceStatus.StatusFeedbacks.Values)) } @@ -425,7 +201,7 @@ var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the nginx deployment from cluster", func() { + It("get the deployment from cluster", func() { Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) if err != nil { @@ -438,38 +214,39 @@ var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the resource bundle with the maestro api", func() { - gotResourceBundle, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, resourceID).Execute() + It("get the resource via maestro api", func() { + gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() Expect(err).ShouldNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*gotResourceBundle.Id).To(Equal(resourceID)) - Expect(*gotResourceBundle.Version).To(Equal(int32(2))) + Expect(*gotResource.Id).To(Equal(resourceID)) + Expect(*gotResource.Version).To(Equal(int32(2))) }) - It("publish a resource bundle spec with delete request using grpc client", func() { - evt := helper.NewEvent(sourceID, "delete_request", agentTestOpts.consumerName, resourceID, deployName, 2, 2) + It("publish a resource delete with grpc client", func() { + evt, err := helper.NewEvent(sourceID, "delete_request", agentTestOpts.consumerName, resourceID, deployName, 2, 2) + Expect(err).ShouldNot(HaveOccurred()) pbEvt := &pbv1.CloudEvent{} - err := grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) + err = grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt) Expect(err).To(BeNil(), "failed to convert spec from cloudevent to protobuf") _, err = grpcClient.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}) Expect(err).ShouldNot(HaveOccurred()) }) - It("Subscribe to the resource bundle status using grpc client", func() { + It("subscribe to the resource status with grpc client", func() { Eventually(func() error { - if resourceBundleStatus.ManifestBundleStatus == nil { - return fmt.Errorf("resource bundle status is empty") + if resourceStatus.ManifestBundleStatus == nil { + return fmt.Errorf("resource status is empty") } - if !meta.IsStatusConditionTrue(resourceBundleStatus.ManifestBundleStatus.Conditions, common.ManifestsDeleted) { - return fmt.Errorf("resource bundle not applied") + if !meta.IsStatusConditionTrue(resourceStatus.ManifestBundleStatus.Conditions, common.ManifestsDeleted) { + return fmt.Errorf("resource not applied") } return nil }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the nginx deployment from cluster", func() { + It("get the deployment from cluster", func() { Eventually(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) if err != nil { @@ -482,8 +259,8 @@ var _ = Describe("GRPC", Ordered, Label("e2e-tests-grpc"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the resource with the maestro api", func() { - _, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, resourceID).Execute() + It("check the resource via maestro api", func() { + _, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() Expect(err).To(HaveOccurred(), "Expected 404 error") Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) }) diff --git a/test/e2e/pkg/resources_test.go b/test/e2e/pkg/resources_test.go index 9fca6f7f..19275410 100644 --- a/test/e2e/pkg/resources_test.go +++ b/test/e2e/pkg/resources_test.go @@ -1,38 +1,32 @@ package e2e_test import ( - "encoding/json" "fmt" "net/http" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" workv1 "open-cluster-management.io/api/work/v1" - "github.com/openshift-online/maestro/pkg/api" - "github.com/openshift-online/maestro/pkg/api/openapi" + "github.com/openshift-online/maestro/pkg/client/cloudevents/grpcsource" ) var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() { Context("Resource CRUD Tests", func() { + workName := fmt.Sprintf("work-%s", rand.String(5)) deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - var resource *openapi.Resource - It("post the nginx resource to the maestro api", func() { - res := helper.NewAPIResource(agentTestOpts.consumerName, deployName, 1) - var resp *http.Response - var err error - resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).ShouldNot(BeEmpty()) - Expect(*resource.Version).To(Equal(int32(1))) + work := helper.NewManifestWork(workName, deployName, "default", 1) + var resourceID string + It("create a resource with source work client", func() { + _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, work, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) @@ -46,161 +40,48 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("get the nginx resource from the maestro api", func() { - gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() + It("get the resource via maestro api", func() { + search := fmt.Sprintf("consumer_name = '%s'", agentTestOpts.consumerName) + gotResourceList, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Search(search).Execute() Expect(err).ShouldNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*gotResource.Id).To(Equal(*resource.Id)) - Expect(*gotResource.Version).To(Equal(*resource.Version)) - }) - - It("patch the nginx resource with the maestro api", func() { - newRes := helper.NewAPIResource(agentTestOpts.consumerName, deployName, 2) - patchedResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, *resource.Id). - ResourcePatchRequest(openapi.ResourcePatchRequest{Version: resource.Version, Manifest: newRes.Manifest}).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*patchedResource.Version).To(Equal(*resource.Version + 1)) - - Eventually(func() error { - deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - return err - } - if *deploy.Spec.Replicas != 2 { - return fmt.Errorf("unexpected replicas, expected 2, got %d", *deploy.Spec.Replicas) - } - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("delete the nginx resource", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - - Eventually(func() error { - _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - return fmt.Errorf("nginx deployment still exists") - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - }) - - Context("Resource Delete Option Tests", func() { - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - var resource *openapi.Resource - It("post the nginx resource to the maestro api", func() { - res := helper.NewAPIResource(agentTestOpts.consumerName, deployName, 1) - res.DeleteOption = map[string]interface{}{"propagationPolicy": "Orphan"} - var resp *http.Response - var err error - resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).ShouldNot(BeEmpty()) - - Eventually(func() error { - deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - return err - } - if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) - } - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) + Expect(len(gotResourceList.Items)).To(Equal(1)) + resourceID = *gotResourceList.Items[0].Id }) - It("delete the nginx resource from the maestro api", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() + It("patch the resource with source work client", func() { + work, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workName, metav1.GetOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - // ensure the "nginx" deployment in the "default" namespace is not deleted - Consistently(func() error { - _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("nginx deployment is deleted") - } - } - return nil - }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) - }) + newWork := work.DeepCopy() + newWork.Spec.Workload.Manifests = []workv1.Manifest{helper.NewManifest(deployName, "default", 2)} - It("delete the nginx deployment", func() { - err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Delete(ctx, deployName, metav1.DeleteOptions{}) + patchData, err := grpcsource.ToWorkPatch(work, newWork) Expect(err).ShouldNot(HaveOccurred()) - Eventually(func() error { - _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - return fmt.Errorf("nginx deployment still exists") - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - }) - - Context("Resource CreateOnly UpdateStrategy Tests", func() { - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - var resource *openapi.Resource - It("post the nginx resource to the maestro api with createOnly updateStrategy", func() { - res := helper.NewAPIResource(agentTestOpts.consumerName, deployName, 1) - res.UpdateStrategy = map[string]interface{}{"type": "CreateOnly"} - var resp *http.Response - var err error - resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() + _, err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Patch(ctx, workName, types.MergePatchType, patchData, metav1.PatchOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).ShouldNot(BeEmpty()) Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) if err != nil { return err } - if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) + if *deploy.Spec.Replicas != 2 { + return fmt.Errorf("unexpected replicas, expected 2, got %d", *deploy.Spec.Replicas) } return nil }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - It("patch the nginx resource", func() { - newRes := helper.NewAPIResource(agentTestOpts.consumerName, deployName, 2) - patchedResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, *resource.Id). - ResourcePatchRequest(openapi.ResourcePatchRequest{Version: resource.Version, Manifest: newRes.Manifest}).Execute() + gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() Expect(err).ShouldNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*patchedResource.Version).To(Equal(*resource.Version + 1)) - - // ensure the "nginx" deployment in the "default" namespace is not updated - Consistently(func() error { - deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - return nil - } - if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) - } - return nil - }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) + Expect(*gotResource.Version).To(Equal(int32(2))) }) - It("delete the nginx resource", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() + It("delete the resource with source work client", func() { + err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) Eventually(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) @@ -212,101 +93,18 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() { } return fmt.Errorf("nginx deployment still exists") }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - }) - - Context("Resource ReadOnly UpdateStrategy Tests via restful api", func() { - var resource *openapi.Resource - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - It("create a nginx deployment in the target cluster", func() { - nginxDeploy := &appsv1.Deployment{} - err := json.Unmarshal([]byte(helper.NewResourceManifestJSON(deployName, 1)), nginxDeploy) - Expect(err).ShouldNot(HaveOccurred()) - _, err = agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Create(ctx, nginxDeploy, metav1.CreateOptions{}) - Expect(err).ShouldNot(HaveOccurred()) - }) - It("post the resource to the maestro api with readonly updateStrategy", func() { - var resp *http.Response - var err error - // post the resource with readonly updateStrategy and foreground delete option should fail - invalidRes := helper.NewReadOnlyAPIResource(agentTestOpts.consumerName, deployName) - invalidRes.DeleteOption = map[string]interface{}{"propagationPolicy": "Foreground"} - resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(invalidRes).Execute() + _, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() Expect(err).Should(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) - - res := helper.NewReadOnlyAPIResource(agentTestOpts.consumerName, deployName) - resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).ShouldNot(BeEmpty()) - }) - - It("get the resource status back", func() { - Eventually(func() error { - res, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() - if err != nil { - return err - } - - // ensure the delete option is set to Orphan - deleteType, ok := res.DeleteOption["propagationPolicy"] - if !ok { - return fmt.Errorf("delete option is not set") - } - if deleteType != "Orphan" { - return fmt.Errorf("delete option is not Orphan") - } - - statusJSON, err := json.Marshal(res.Status) - if err != nil { - return err - } - - resourceStatus := &api.ResourceStatus{} - err = json.Unmarshal(statusJSON, resourceStatus) - if err != nil { - return err - } - - if resourceStatus.ContentStatus != nil { - conditions := resourceStatus.ContentStatus["conditions"].([]interface{}) - if len(conditions) > 0 { - return nil - } - } - - return fmt.Errorf("contentStatus should not be empty") - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - }) - - It("delete the readonly resource", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - - err = agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Delete(ctx, deployName, metav1.DeleteOptions{}) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() error { - _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return err - } - return fmt.Errorf("nginx deployment still exists") - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) }) }) - Context("Resource ReadOnly UpdateStrategy Tests via gRPC", func() { + Context("Resource ReadOnly Tests", func() { workName := "work-readonly-" + rand.String(5) secretName := "auth-" + rand.String(5) manifest := fmt.Sprintf("{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"name\":\"%s\",\"namespace\":\"default\"}}", secretName) - It("create a secret in the target cluster", func() { + It("create the secret in the target cluster", func() { _, err := agentTestOpts.kubeClientSet.CoreV1().Secrets("default").Create(ctx, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, @@ -319,7 +117,7 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() { Expect(err).ShouldNot(HaveOccurred()) }) - It("post the resource bundle via gRPC client", func() { + It("post the resource with source work client", func() { work := &workv1.ManifestWork{ ObjectMeta: metav1.ObjectMeta{ Name: workName, @@ -365,17 +163,18 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() { }, 5*time.Minute, 5*time.Second).ShouldNot(HaveOccurred()) }) - It("get the resource via restful API", func() { - gotResourceBundleList, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx).Execute() + It("get the resource via maestro API", func() { + search := fmt.Sprintf("consumer_name = '%s'", agentTestOpts.consumerName) + gotResourceList, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Search(search).Execute() Expect(err).ShouldNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(len(gotResourceBundleList.Items)).To(Equal(1)) - resourceBundle := gotResourceBundleList.Items[0] - Expect(resourceBundle.Metadata["creationTimestamp"]).ShouldNot(BeEmpty()) - gotResourceBundle, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, *resourceBundle.Id).Execute() + Expect(len(gotResourceList.Items)).To(Equal(1)) + resource := gotResourceList.Items[0] + Expect(resource.Metadata["creationTimestamp"]).ShouldNot(BeEmpty()) + gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() Expect(err).ShouldNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(gotResourceBundle.Metadata["creationTimestamp"]).ShouldNot(BeEmpty()) + Expect(gotResource.Metadata["creationTimestamp"]).ShouldNot(BeEmpty()) }) It("get the resource status back", func() { @@ -401,7 +200,7 @@ var _ = Describe("Resources", Ordered, Label("e2e-tests-resources"), func() { }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("delete the readonly resource", func() { + It("delete the readonly resource with source work client", func() { err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) diff --git a/test/e2e/pkg/serverside_test.go b/test/e2e/pkg/serverside_test.go index 0fa261ba..029bc0f6 100644 --- a/test/e2e/pkg/serverside_test.go +++ b/test/e2e/pkg/serverside_test.go @@ -1,7 +1,6 @@ package e2e_test import ( - "encoding/json" "fmt" "net/http" "time" @@ -9,9 +8,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/openshift-online/maestro/pkg/api" - "github.com/openshift-online/maestro/pkg/api/openapi" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -21,145 +17,147 @@ import ( workv1 "open-cluster-management.io/api/work/v1" ) -const sleepJob = ` -{ - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "%s", - "namespace": "default" - }, - "spec": { - "template": { - "spec": { - "containers": [ - { - "name": "sleep", - "image": "busybox:1.36", - "command": [ - "/bin/sh", - "-c", - "sleep 10" - ] - } - ], - "restartPolicy": "Never" - } - }, - "backoffLimit": 4 - } -} -` - -var _ = Describe("Server Side Apply", Ordered, Label("e2e-tests-serverside-apply"), func() { - It("Apply a job with maestro", func() { +var _ = Describe("ServerSideApply", Ordered, Label("e2e-tests-ssa"), func() { + Context("Resource ServerSideApply Tests", func() { // The kube-apiserver will set a default selector and label on the Pod of Job if the job does not have // spec.Selector, these fields are immutable, if we use update strategy to apply Job, it will report // AppliedManifestFailed. The maestro uses the server side strategy to apply a resource with ManifestWork // by default, this will avoid this. - manifest := map[string]interface{}{} + workName := "work-ssa-" + rand.String(5) sleepJobName := fmt.Sprintf("sleep-%s", rand.String(5)) - err := json.Unmarshal([]byte(fmt.Sprintf(sleepJob, sleepJobName)), &manifest) - Expect(err).ShouldNot(HaveOccurred()) - - res := openapi.Resource{ - Manifest: manifest, - ConsumerName: &agentTestOpts.consumerName, - GroupResource: map[string]interface{}{ - "group": "batch", - "resource": "jobs", - }, - } - - created, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*created.Id).ShouldNot(BeEmpty()) - - resourceID := *created.Id - Eventually(func() error { - found, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resourceID).Execute() - if err != nil { - return err - } - - if found.Status == nil { - return fmt.Errorf("the resource %s status is nil", resourceID) - } - - statusJSON, err := json.Marshal(found.Status) - if err != nil { - return fmt.Errorf("failed to marshal status to JSON: %v", err) - } - resourceStatus := &api.ResourceStatus{} - if err := json.Unmarshal(statusJSON, resourceStatus); err != nil { - return fmt.Errorf("failed to unmarshal status JSON to ResourceStatus: %v", err) - } - - conditions := resourceStatus.ReconcileStatus.Conditions - - if meta.IsStatusConditionFalse(conditions, workv1.WorkApplied) { - return fmt.Errorf("unexpected condition %v for resource %s", conditions, resourceID) - } - - if meta.IsStatusConditionFalse(conditions, workv1.WorkAvailable) { - return fmt.Errorf("unexpected condition %v for resource %s", conditions, resourceID) - } - - if meta.IsStatusConditionFalse(conditions, "StatusFeedbackSynced") { - return fmt.Errorf("unexpected condition %v for resource %s", conditions, resourceID) + manifest := fmt.Sprintf("{\"apiVersion\":\"batch/v1\",\"kind\":\"Job\",\"metadata\":{\"name\":\"%s\",\"namespace\":\"default\"},\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"sleep\",\"image\":\"busybox:1.36\",\"command\":[\"/bin/sh\",\"-c\",\"sleep 10\"]}],\"restartPolicy\":\"Never\"}},\"backoffLimit\":4}}", sleepJobName) + It("create the resource with source work client", func() { + work := &workv1.ManifestWork{ + ObjectMeta: metav1.ObjectMeta{ + Name: workName, + }, + Spec: workv1.ManifestWorkSpec{ + Workload: workv1.ManifestsTemplate{ + Manifests: []workv1.Manifest{ + { + RawExtension: runtime.RawExtension{ + Raw: []byte(manifest), + }, + }, + }, + }, + ManifestConfigs: []workv1.ManifestConfigOption{ + { + ResourceIdentifier: workv1.ResourceIdentifier{ + Group: "batch", + Resource: "jobs", + Name: sleepJobName, + Namespace: "default", + }, + FeedbackRules: []workv1.FeedbackRule{ + { + Type: workv1.JSONPathsType, + JsonPaths: []workv1.JsonPath{ + { + Name: "status", + Path: ".status", + }, + }, + }, + }, + UpdateStrategy: &workv1.UpdateStrategy{ + Type: workv1.UpdateStrategyTypeServerSideApply, + }, + }, + }, + }, } - return nil - }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - - // cleanup the job - resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, resourceID).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - }) - - It("Apply a nested work with SSA", func() { - workName := fmt.Sprintf("ssa-work-%s", rand.String(5)) - nestedWorkName := fmt.Sprintf("nested-work-%s", rand.String(5)) - nestedWorkNamespace := "default" - - work := NewNestedManifestWork(nestedWorkNamespace, workName, nestedWorkName) - Eventually(func() error { _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, work, metav1.CreateOptions{}) - return err - }, 5*time.Minute, 5*time.Second).ShouldNot(HaveOccurred()) - - // make sure the nested work is created - Eventually(func() error { - _, err := agentTestOpts.workClientSet.WorkV1().ManifestWorks(nestedWorkNamespace).Get(ctx, nestedWorkName, metav1.GetOptions{}) - if err != nil { - return err - } + Expect(err).NotTo(HaveOccurred()) + }) + + It("get the resource via maestro api", func() { + search := fmt.Sprintf("consumer_name = '%s'", agentTestOpts.consumerName) + gotResourceList, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Search(search).Execute() + Expect(err).ShouldNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(len(gotResourceList.Items)).To(Equal(1)) + resource := gotResourceList.Items[0] + Expect(resource.Metadata["creationTimestamp"]).ShouldNot(BeEmpty()) + }) + + It("get the resource status back", func() { + Eventually(func() error { + work, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workName, metav1.GetOptions{}) + if err != nil { + return err + } + if work.CreationTimestamp.Time.IsZero() { + return fmt.Errorf("work creationTimestamp is empty") + } + + conditions := work.Status.Conditions + if meta.IsStatusConditionFalse(conditions, workv1.WorkApplied) { + return fmt.Errorf("unexpected condition %v", conditions) + } + + if meta.IsStatusConditionFalse(conditions, workv1.WorkAvailable) { + return fmt.Errorf("unexpected condition %v", conditions) + } + + if meta.IsStatusConditionFalse(conditions, "StatusFeedbackSynced") { + return fmt.Errorf("unexpected condition %v", conditions) + } + + return nil + }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) + }) + + It("delete the resource with source work client", func() { + err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) - return nil - }, 30*time.Second, time.Second).ShouldNot(HaveOccurred()) + Context("Nested Work ServerSideApply Tests", func() { + It("create a nested work with SSA", func() { + workName := fmt.Sprintf("ssa-work-%s", rand.String(5)) + nestedWorkName := fmt.Sprintf("nested-work-%s", rand.String(5)) + nestedWorkNamespace := "default" - // make sure the nested work is not updated - Consistently(func() error { - nestedWork, err := agentTestOpts.workClientSet.WorkV1().ManifestWorks(nestedWorkNamespace).Get(ctx, nestedWorkName, metav1.GetOptions{}) - if err != nil { + work := newNestedManifestWork(nestedWorkNamespace, workName, nestedWorkName) + Eventually(func() error { + _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, work, metav1.CreateOptions{}) return err - } - - if nestedWork.Generation != 1 { - return fmt.Errorf("nested work generation is changed to %d", nestedWork.Generation) - } - - return nil - }, 1*time.Minute, 1*time.Second).Should(BeNil()) - - err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) - Expect(err).ShouldNot(HaveOccurred()) + }, 5*time.Minute, 5*time.Second).ShouldNot(HaveOccurred()) + + // make sure the nested work is created + Eventually(func() error { + _, err := agentTestOpts.workClientSet.WorkV1().ManifestWorks(nestedWorkNamespace).Get(ctx, nestedWorkName, metav1.GetOptions{}) + if err != nil { + return err + } + + return nil + }, 30*time.Second, time.Second).ShouldNot(HaveOccurred()) + + // make sure the nested work is not updated + Consistently(func() error { + nestedWork, err := agentTestOpts.workClientSet.WorkV1().ManifestWorks(nestedWorkNamespace).Get(ctx, nestedWorkName, metav1.GetOptions{}) + if err != nil { + return err + } + + if nestedWork.Generation != 1 { + return fmt.Errorf("nested work generation is changed to %d", nestedWork.Generation) + } + + return nil + }, 1*time.Minute, 1*time.Second).Should(BeNil()) + + err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) + Expect(err).ShouldNot(HaveOccurred()) + }) }) }) -func NewNestedManifestWork(nestedWorkNamespace, name, nestedWorkName string) *workv1.ManifestWork { +func newNestedManifestWork(nestedWorkNamespace, name, nestedWorkName string) *workv1.ManifestWork { nestedWork := &workv1.ManifestWork{ TypeMeta: metav1.TypeMeta{ APIVersion: "work.open-cluster-management.io/v1", diff --git a/test/e2e/pkg/sourceclient_test.go b/test/e2e/pkg/sourceclient_test.go index 9f40225d..29c7ac89 100644 --- a/test/e2e/pkg/sourceclient_test.go +++ b/test/e2e/pkg/sourceclient_test.go @@ -25,7 +25,7 @@ import ( "open-cluster-management.io/sdk-go/pkg/cloudevents/work/common" ) -var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source-work-client"), func() { +var _ = Describe("SourceWorkClient", Ordered, Label("e2e-tests-source-work-client"), func() { Context("Update an obsolete work", func() { var workName string @@ -51,7 +51,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- }) - It("Should return an error when updating an obsolete work", func() { + It("should return error when updating an obsolete work", func() { By("update a work by work client") work, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workName, metav1.GetOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -79,7 +79,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- }) }) - Context("Watch work status with gRPC source ManifestWork client", func() { + Context("Watch work status with source work client", func() { var watcherCtx context.Context var watcherCancel context.CancelFunc @@ -120,8 +120,8 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- watcherCancel() }) - It("The work status should be watched", func() { - By("create a work client for watch") + It("the work status should be watched", func() { + By("create a work watcher client") watcherClient, err := grpcsource.NewMaestroGRPCSourceWorkClient( watcherCtx, apiClient, @@ -130,12 +130,12 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- ) Expect(err).ShouldNot(HaveOccurred()) - By("start watching") + By("start status watching") watcher, err := watcherClient.ManifestWorks(agentTestOpts.consumerName).Watch(watcherCtx, metav1.ListOptions{}) Expect(err).ShouldNot(HaveOccurred()) result := StartWatch(watcherCtx, watcher) - By("create a work by work client") + By("create a work with source work client") workName := "work-" + rand.String(5) _, err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, NewManifestWork(workName), metav1.CreateOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -143,7 +143,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- // wait for few seconds to ensure the creation is finished <-time.After(5 * time.Second) - By("update a work by work client") + By("update a work with source work client") work, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workName, metav1.GetOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -158,7 +158,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- // wait for few seconds to ensure the work status is updated by agent <-time.After(5 * time.Second) - By("delete the work by work client") + By("delete the work with source work client") err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -167,7 +167,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("The watchers with different namespace", func() { + It("the watchers for different namespace", func() { watcherClient, err := grpcsource.NewMaestroGRPCSourceWorkClient( watcherCtx, apiClient, @@ -191,7 +191,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- Expect(err).ShouldNot(HaveOccurred()) otherConsumerWatcherResult := StartWatch(watcherCtx, otherConsumerWatcher) - By("create a work by work client") + By("create a work with source work client") workName := "work-" + rand.String(5) _, err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, NewManifestWork(workName), metav1.CreateOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -199,7 +199,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- // wait for few seconds to ensure the creation is finished <-time.After(5 * time.Second) - By("delete the work by work client") + By("delete the work with source work client") err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -219,7 +219,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("The watchers with label selector", func() { + It("the watchers with label selector", func() { watcherClient, err := grpcsource.NewMaestroGRPCSourceWorkClient( watcherCtx, apiClient, @@ -228,14 +228,14 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- ) Expect(err).ShouldNot(HaveOccurred()) - By("start watching with label app=test") + By("start watching with label") watcher, err := watcherClient.ManifestWorks(agentTestOpts.consumerName).Watch(watcherCtx, metav1.ListOptions{ LabelSelector: "app=test", }) Expect(err).ShouldNot(HaveOccurred()) result := StartWatch(watcherCtx, watcher) - By("create a work by work client") + By("create a work with source work client") workName := "work-" + rand.String(5) work := NewManifestWorkWithLabels(workName, map[string]string{"app": "test"}) _, err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, work, metav1.CreateOptions{}) @@ -244,7 +244,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- // wait for few seconds to ensure the creation is finished <-time.After(5 * time.Second) - By("delete the work by work client") + By("delete the work with source work client") err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) @@ -254,7 +254,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- }) }) - Context("List works with gRPC source ManifestWork client", func() { + Context("List works with source work client", func() { var workName string var prodWorkName string var testWorkAName string @@ -329,7 +329,7 @@ var _ = Describe("Source ManifestWork Client", Ordered, Label("e2e-tests-source- }, 2*time.Minute, 2*time.Second).ShouldNot(HaveOccurred()) }) - It("List works with options", func() { + It("list works with options", func() { By("list all works") works, err := sourceWorkClient.ManifestWorks(metav1.NamespaceAll).List(ctx, metav1.ListOptions{}) Expect(err).ShouldNot(HaveOccurred()) diff --git a/test/e2e/pkg/spec_resync_test.go b/test/e2e/pkg/spec_resync_test.go index a6a7aa6a..752d064b 100644 --- a/test/e2e/pkg/spec_resync_test.go +++ b/test/e2e/pkg/spec_resync_test.go @@ -2,33 +2,33 @@ package e2e_test import ( "fmt" - "net/http" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/openshift-online/maestro/pkg/api/openapi" + "github.com/openshift-online/maestro/pkg/client/cloudevents/grpcsource" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" + workv1 "open-cluster-management.io/api/work/v1" ) var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-resync-restart"), func() { - Context("Resource resync resource spec after maestro agent restarts", func() { + Context("Resync resource spec after maestro agent restarts", func() { var maestroAgentReplicas int - var resourceA, resourceB, resourceC *openapi.Resource - deployA := fmt.Sprintf("nginx-%s", rand.String(5)) - deployB := fmt.Sprintf("nginx-%s", rand.String(5)) - deployC := fmt.Sprintf("nginx-%s", rand.String(5)) - It("post the nginx A resource to the maestro api", func() { - res := helper.NewAPIResource(agentTestOpts.consumerName, deployA, 1) - var resp *http.Response - var err error - resourceA, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resourceA.Id).ShouldNot(BeEmpty()) + deployA := fmt.Sprintf("nginx-a-%s", rand.String(5)) + workNameA := fmt.Sprintf("work-a-%s", rand.String(5)) + workA := helper.NewManifestWork(workNameA, deployA, "default", 1) + deployB := fmt.Sprintf("nginx-b-%s", rand.String(5)) + workNameB := fmt.Sprintf("work-b-%s", rand.String(5)) + workB := helper.NewManifestWork(workNameB, deployB, "default", 1) + deployC := fmt.Sprintf("nginx-c-%s", rand.String(5)) + workNameC := fmt.Sprintf("work-c-%s", rand.String(5)) + workC := helper.NewManifestWork(workNameC, deployC, "default", 1) + It("create resource A with source work client", func() { + _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, workA, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) @@ -36,20 +36,15 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res return err } if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas for nginx A deployment %s, expected 1, got %d", deployA, *deploy.Spec.Replicas) + return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) } return nil }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("post the nginx B resource to the maestro api", func() { - res := helper.NewAPIResource(agentTestOpts.consumerName, deployB, 1) - var resp *http.Response - var err error - resourceB, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resourceB.Id).ShouldNot(BeEmpty()) + It("create resource B with source work client", func() { + _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, workB, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployB, metav1.GetOptions{}) @@ -57,7 +52,7 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res return err } if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas for nginx B deployment %s, expected 1, got %d", deployB, *deploy.Spec.Replicas) + return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) } return nil }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) @@ -90,16 +85,21 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("patch the nginx A resource", func() { - newRes := helper.NewAPIResource(agentTestOpts.consumerName, deployA, 2) - patchedResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, *resourceA.Id). - ResourcePatchRequest(openapi.ResourcePatchRequest{Version: resourceA.Version, Manifest: newRes.Manifest}).Execute() + It("patch the resource A with source work client", func() { + workA, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workNameA, metav1.GetOptions{}) + Expect(err).ShouldNot(HaveOccurred()) + + newWorkA := workA.DeepCopy() + newWorkA.Spec.Workload.Manifests = []workv1.Manifest{helper.NewManifest(deployA, "default", 2)} + + patchData, err := grpcsource.ToWorkPatch(workA, newWorkA) + Expect(err).ShouldNot(HaveOccurred()) + + _, err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Patch(ctx, workNameA, types.MergePatchType, patchData, metav1.PatchOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*patchedResource.Version).To(Equal(*resourceA.Version + 1)) }) - It("ensure the nginx A resource is not updated", func() { + It("ensure the nginx A deployment is not updated", func() { Consistently(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) if err != nil { @@ -112,13 +112,12 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("delete the nginx B resource", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resourceB.Id).Execute() + It("delete the resource B with source work client", func() { + err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workNameB, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) }) - It("ensure the nginx B resource is not deleted", func() { + It("ensure the nginx B deployment is not deleted", func() { Consistently(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployB, metav1.GetOptions{}) if err != nil { @@ -130,17 +129,12 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("post the nginx C resource to the maestro api", func() { - res := helper.NewAPIResource(agentTestOpts.consumerName, deployC, 1) - var resp *http.Response - var err error - resourceC, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resourceC.Id).ShouldNot(BeEmpty()) + It("create resource C with source work client", func() { + _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, workC, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) }) - It("ensure the nginx C resource is not created", func() { + It("ensure the nginx C deployment is not created", func() { Consistently(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployC, metav1.GetOptions{}) if err == nil { @@ -181,7 +175,7 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("ensure the nginx A resource is updated", func() { + It("ensure the nginx A deployment is updated", func() { Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) if err != nil { @@ -194,7 +188,7 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 3*time.Minute, 3*time.Second).ShouldNot(HaveOccurred()) }) - It("ensure the nginx B resource is deleted", func() { + It("ensure the nginx B deployment is deleted", func() { Eventually(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployB, metav1.GetOptions{}) if err != nil { @@ -207,7 +201,7 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("ensure the nginx C resource is created", func() { + It("ensure the nginx C deployment is created", func() { Eventually(func() error { deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployC, metav1.GetOptions{}) if err != nil { @@ -220,10 +214,12 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) }) - It("delete the nginx A and nginx C resources", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resourceA.Id).Execute() + It("delete the nginx A and nginx C resource", func() { + err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workNameA, metav1.DeleteOptions{}) + Expect(err).ShouldNot(HaveOccurred()) + + err = sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workNameC, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) Eventually(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) @@ -236,10 +232,6 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res return fmt.Errorf("nginx A deployment %s still exists", deployA) }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resourceC.Id).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - Eventually(func() error { _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployC, metav1.GetOptions{}) if err != nil { @@ -253,363 +245,3 @@ var _ = Describe("Spec Resync After Restart", Ordered, Label("e2e-tests-spec-res }) }) }) - -// var _ = Describe("Spec Resync After Reconnect", Ordered, Label("e2e-tests-spec-resync-reconnect"), func() { -// Context("Resource resync resource spec after maestro agent reconnects", func() { -// var maestroServerReplicas, mqttReplicas int -// var resourceA, resourceB, resourceC *openapi.Resource -// deployA := fmt.Sprintf("nginx-%s", rand.String(5)) -// deployB := fmt.Sprintf("nginx-%s", rand.String(5)) -// deployC := fmt.Sprintf("nginx-%s", rand.String(5)) -// It("post the nginx A resource to the maestro api", func() { -// res := helper.NewAPIResource(agentTestOpts.consumerName, deployA, 1) -// var resp *http.Response -// var err error -// resourceA, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusCreated)) -// Expect(*resourceA.Id).ShouldNot(BeEmpty()) - -// Eventually(func() error { -// deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) -// if err != nil { -// return err -// } -// if *deploy.Spec.Replicas != 1 { -// return fmt.Errorf("unexpected replicas for nginx A deployment %s, expected 1, got %d", deployA, *deploy.Spec.Replicas) -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("post the nginx B resource to the maestro api", func() { -// res := helper.NewAPIResource(agentTestOpts.consumerName, deployB, 1) -// var resp *http.Response -// var err error -// resourceB, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusCreated)) -// Expect(*resourceB.Id).ShouldNot(BeEmpty()) - -// Eventually(func() error { -// deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployB, metav1.GetOptions{}) -// if err != nil { -// return err -// } -// if *deploy.Spec.Replicas != 1 { -// return fmt.Errorf("unexpected replicas for nginx B deployment %s, expected 1, got %d", deployB, *deploy.Spec.Replicas) -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("delete the grpc-broker service for agent", func() { -// err := serverTestOpts.kubeClientSet.CoreV1().Services(serverTestOpts.serverNamespace).Delete(ctx, "maestro-grpc-broker", metav1.DeleteOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("delete the mqtt-broker service for agent", func() { -// err := serverTestOpts.kubeClientSet.CoreV1().Services(serverTestOpts.serverNamespace).Delete(ctx, "maestro-mqtt-agent", metav1.DeleteOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("rollout maestro server", func() { -// deploy, err := serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Get(ctx, "maestro", metav1.GetOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// maestroServerReplicas = int(*deploy.Spec.Replicas) -// deploy, err = serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Patch(ctx, "maestro", types.MergePatchType, []byte(`{"spec":{"replicas":0}}`), metav1.PatchOptions{ -// FieldManager: "serverTestOpts.kubeClientSet", -// }) -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(*deploy.Spec.Replicas).To(Equal(int32(0))) - -// // ensure no running maestro server pods -// Eventually(func() error { -// pods, err := serverTestOpts.kubeClientSet.CoreV1().Pods(serverTestOpts.serverNamespace).List(ctx, metav1.ListOptions{ -// LabelSelector: "app=maestro", -// }) -// if err != nil { -// return err -// } -// if len(pods.Items) > 0 { -// return fmt.Errorf("maestro server pods still running") -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - -// // patch maestro server replicas to maestroServerReplicas -// deploy, err = serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Patch(ctx, "maestro", types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, maestroServerReplicas)), metav1.PatchOptions{ -// FieldManager: "serverTestOpts.kubeClientSet", -// }) -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(*deploy.Spec.Replicas).To(Equal(int32(maestroServerReplicas))) - -// // ensure maestro server pod is up and running -// Eventually(func() error { -// pods, err := serverTestOpts.kubeClientSet.CoreV1().Pods(serverTestOpts.serverNamespace).List(ctx, metav1.ListOptions{ -// LabelSelector: "app=maestro", -// }) -// if err != nil { -// return err -// } -// if len(pods.Items) != maestroServerReplicas { -// return fmt.Errorf("unexpected maestro server pod count, expected %d, got %d", maestroServerReplicas, len(pods.Items)) -// } -// for _, pod := range pods.Items { -// if pod.Status.Phase != "Running" { -// return fmt.Errorf("maestro server pod not in running state") -// } -// if pod.Status.ContainerStatuses[0].State.Running == nil { -// return fmt.Errorf("maestro server container not in running state") -// } -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("rollout the mqtt-broker", func() { -// deploy, err := serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Get(ctx, "maestro-mqtt", metav1.GetOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// mqttReplicas = int(*deploy.Spec.Replicas) -// deploy, err = serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Patch(ctx, "maestro-mqtt", types.MergePatchType, []byte(`{"spec":{"replicas":0}}`), metav1.PatchOptions{ -// FieldManager: "serverTestOpts.kubeClientSet", -// }) -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(*deploy.Spec.Replicas).To(Equal(int32(0))) - -// // ensure no running mqtt-broker pods -// Eventually(func() error { -// pods, err := serverTestOpts.kubeClientSet.CoreV1().Pods(serverTestOpts.serverNamespace).List(ctx, metav1.ListOptions{ -// LabelSelector: "name=maestro-mqtt", -// }) -// if err != nil { -// return err -// } -// if len(pods.Items) > 0 { -// return fmt.Errorf("maestro-mqtt pods still running") -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - -// // patch mqtt-broker replicas to mqttReplicas -// deploy, err = serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Patch(ctx, "maestro-mqtt", types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, mqttReplicas)), metav1.PatchOptions{ -// FieldManager: "serverTestOpts.kubeClientSet", -// }) -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(*deploy.Spec.Replicas).To(Equal(int32(mqttReplicas))) - -// // ensure mqtt-broker pod is up and running -// Eventually(func() error { -// pods, err := serverTestOpts.kubeClientSet.CoreV1().Pods(serverTestOpts.serverNamespace).List(ctx, metav1.ListOptions{ -// LabelSelector: "name=maestro-mqtt", -// }) -// if err != nil { -// return err -// } -// if len(pods.Items) != mqttReplicas { -// return fmt.Errorf("unexpected maestro-mqtt pod count, expected %d, got %d", mqttReplicas, len(pods.Items)) -// } -// for _, pod := range pods.Items { -// if pod.Status.Phase != "Running" { -// return fmt.Errorf("maestro-mqtt pod not in running state") -// } -// if pod.Status.ContainerStatuses[0].State.Running == nil { -// return fmt.Errorf("maestro-mqtt container not in running state") -// } -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("patch the nginx A resource", func() { -// newRes := helper.NewAPIResource(agentTestOpts.consumerName, deployA, 2) -// Eventually(func() error { -// patchedResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, *resourceA.Id). -// ResourcePatchRequest(openapi.ResourcePatchRequest{Version: resourceA.Version, Manifest: newRes.Manifest}).Execute() -// if err != nil { -// return err -// } -// if resp.StatusCode != http.StatusOK { -// return fmt.Errorf("unexpected status code, expected 200, got %d", resp.StatusCode) -// } -// if *patchedResource.Version != *resourceA.Version+1 { -// return fmt.Errorf("unexpected version, expected %d, got %d", *resourceA.Version+1, *patchedResource.Version) -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("ensure the nginx A resource is not updated", func() { -// Consistently(func() error { -// deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) -// if err != nil { -// return nil -// } -// if *deploy.Spec.Replicas != 1 { -// return fmt.Errorf("unexpected replicas for nginx A deployment %s, expected 1, got %d", deployA, *deploy.Spec.Replicas) -// } -// return nil -// }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("delete the nginx B resource", func() { -// resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resourceB.Id).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) -// }) - -// It("ensure the nginx B resource is not deleted", func() { -// Consistently(func() error { -// _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployB, metav1.GetOptions{}) -// if err != nil { -// if errors.IsNotFound(err) { -// return fmt.Errorf("nginx B deployment %s is deleted", deployB) -// } -// } -// return nil -// }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("post the nginx C resource to the maestro api", func() { -// res := helper.NewAPIResource(agentTestOpts.consumerName, deployC, 1) -// var resp *http.Response -// var err error -// resourceC, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusCreated)) -// Expect(*resourceC.Id).ShouldNot(BeEmpty()) -// }) - -// It("ensure the nginx C resource is not created", func() { -// Consistently(func() error { -// _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployC, metav1.GetOptions{}) -// if err == nil { -// return fmt.Errorf("nginx C deployment %s is created", deployC) -// } -// return nil -// }, 10*time.Second, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("recreate the mqtt-broker service for agent", func() { -// mqttAgentService := &corev1.Service{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "maestro-mqtt-agent", -// Namespace: serverTestOpts.serverNamespace, -// }, -// Spec: corev1.ServiceSpec{ -// Selector: map[string]string{ -// "name": "maestro-mqtt", -// }, -// Ports: []corev1.ServicePort{ -// { -// Name: "mosquitto", -// Protocol: corev1.ProtocolTCP, -// Port: 1883, -// TargetPort: intstr.FromInt(1883), -// }, -// }, -// Type: corev1.ServiceTypeClusterIP, -// }, -// } - -// _, err := serverTestOpts.kubeClientSet.CoreV1().Services(serverTestOpts.serverNamespace).Create(ctx, mqttAgentService, metav1.CreateOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("recreate the grpc-broker service for agent", func() { -// grpcBrokerService := &corev1.Service{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "maestro-grpc-broker", -// Namespace: serverTestOpts.serverNamespace, -// }, -// Spec: corev1.ServiceSpec{ -// Selector: map[string]string{ -// "app": "maestro", -// }, -// Ports: []corev1.ServicePort{ -// { -// Name: "grpc-broker", -// Protocol: corev1.ProtocolTCP, -// Port: 8091, -// TargetPort: intstr.FromInt(8091), -// }, -// }, -// Type: corev1.ServiceTypeClusterIP, -// }, -// } -// _, err := serverTestOpts.kubeClientSet.CoreV1().Services(serverTestOpts.serverNamespace).Create(ctx, grpcBrokerService, metav1.CreateOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("ensure the nginx A resource is updated", func() { -// Eventually(func() error { -// deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) -// if err != nil { -// return err -// } -// if *deploy.Spec.Replicas != 2 { -// return fmt.Errorf("unexpected replicas for nginx A deployment %s, expected 2, got %d", deployA, *deploy.Spec.Replicas) -// } -// return nil -// }, 3*time.Minute, 3*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("ensure the nginx B resource is deleted", func() { -// Eventually(func() error { -// _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployB, metav1.GetOptions{}) -// if err != nil { -// if errors.IsNotFound(err) { -// return nil -// } -// return err -// } -// return fmt.Errorf("nginx B deployment %s still exists", deployB) -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("ensure the nginx C resource is created", func() { -// Eventually(func() error { -// deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployC, metav1.GetOptions{}) -// if err != nil { -// return err -// } -// if *deploy.Spec.Replicas != 1 { -// return fmt.Errorf("unexpected replicas for nginx C deployment %s, expected 1, got %d", deployC, *deploy.Spec.Replicas) -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("delete the nginx A and nginx C resources", func() { -// resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resourceA.Id).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - -// Eventually(func() error { -// _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployA, metav1.GetOptions{}) -// if err != nil { -// if errors.IsNotFound(err) { -// return nil -// } -// return err -// } -// return fmt.Errorf("nginx A deployment %s still exists", deployA) -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - -// resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resourceC.Id).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - -// Eventually(func() error { -// _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployC, metav1.GetOptions{}) -// if err != nil { -// if errors.IsNotFound(err) { -// return nil -// } -// return err -// } -// return fmt.Errorf("nginx C deployment %s still exists", deployC) -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) -// }) -// }) diff --git a/test/e2e/pkg/status_resync_test.go b/test/e2e/pkg/status_resync_test.go index 171d3af5..4a0294dd 100644 --- a/test/e2e/pkg/status_resync_test.go +++ b/test/e2e/pkg/status_resync_test.go @@ -1,15 +1,12 @@ package e2e_test import ( - "encoding/json" "fmt" - "net/http" "strings" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/openshift-online/maestro/pkg/api/openapi" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,49 +15,48 @@ import ( ) var _ = Describe("Status Resync After Restart", Ordered, Label("e2e-tests-status-resync-restart"), func() { - Context("Resource resync resource status after maestro server restarts", func() { + Context("Resync resource status after maestro server restarts", func() { var maestroServerReplicas int - var resource *openapi.Resource - name := fmt.Sprintf("nginx-%s", rand.String(5)) - It("post the nginx resource with non-default service account to the maestro api", func() { - res := helper.NewAPIResourceWithSA(agentTestOpts.consumerName, name, name, 1) - var resp *http.Response - var err error - resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).ShouldNot(BeEmpty()) + workName := fmt.Sprintf("work-%s", rand.String(5)) + deployName := fmt.Sprintf("nginx-%s", rand.String(5)) + work := helper.NewManifestWork(workName, deployName, deployName, 1) + It("create a resource with source work client", func() { + _, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Create(ctx, work, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) Eventually(func() error { - deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, name, metav1.GetOptions{}) + deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, deployName, metav1.GetOptions{}) if err != nil { return err } if *deploy.Spec.Replicas != 1 { - return fmt.Errorf("unexpected replicas for nginx deployment %s, expected 1, got %d", name, *deploy.Spec.Replicas) + return fmt.Errorf("unexpected replicas, expected 1, got %d", *deploy.Spec.Replicas) } return nil }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() - Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*gotResource.Id).To(Equal(*resource.Id)) - Expect(*gotResource.Version).To(Equal(*resource.Version)) - Eventually(func() error { - gotResource, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() + work, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workName, metav1.GetOptions{}) if err != nil { return err } - statusJSON, err := json.Marshal(gotResource.Status) - if err != nil { - return err + if work.CreationTimestamp.Time.IsZero() { + return fmt.Errorf("work creationTimestamp is empty") } - if !strings.Contains(string(statusJSON), "error looking up service account default/nginx") { - return fmt.Errorf("unexpected status, expected error looking up service account default/nginx, got %s", string(statusJSON)) + + manifests := work.Status.ResourceStatus.Manifests + if len(manifests) > 0 && len(manifests[0].StatusFeedbacks.Values) != 0 { + feedback := manifests[0].StatusFeedbacks.Values + if feedback[0].Name == "status" { + feedbackRaw := *feedback[0].Value.JsonRaw + if strings.Contains(feedbackRaw, "error looking up service account default/nginx") { + return nil + } + } + } - return nil + + return fmt.Errorf("unexpected status, expected error looking up service account default/nginx") }, 2*time.Minute, 2*time.Second).ShouldNot(HaveOccurred()) }) @@ -94,13 +90,13 @@ var _ = Describe("Status Resync After Restart", Ordered, Label("e2e-tests-status It("create serviceaccount for nginx deployment", func() { _, err := agentTestOpts.kubeClientSet.CoreV1().ServiceAccounts("default").Create(ctx, &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: name, + Name: deployName, }, }, metav1.CreateOptions{}) Expect(err).ShouldNot(HaveOccurred()) // delete the nginx deployment to tigger recreating - err = agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Delete(ctx, name, metav1.DeleteOptions{}) + err = agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Delete(ctx, deployName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) }) @@ -137,31 +133,36 @@ var _ = Describe("Status Resync After Restart", Ordered, Label("e2e-tests-status It("ensure the resource status is resynced", func() { Eventually(func() error { - gotResource, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() + work, err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Get(ctx, workName, metav1.GetOptions{}) if err != nil { return err } - if _, ok := gotResource.Status["ContentStatus"]; !ok { - return fmt.Errorf("unexpected status, expected contains ContentStatus, got %v", gotResource.Status) + if work.CreationTimestamp.Time.IsZero() { + return fmt.Errorf("work creationTimestamp is empty") } - statusJSON, err := json.Marshal(gotResource.Status) - if err != nil { - return err - } - if strings.Contains(string(statusJSON), "error looking up service account default/nginx") { - return fmt.Errorf("unexpected status, should not contain error looking up service account default/nginx, got %s", string(statusJSON)) + + manifests := work.Status.ResourceStatus.Manifests + if len(manifests) > 0 && len(manifests[0].StatusFeedbacks.Values) != 0 { + feedback := manifests[0].StatusFeedbacks.Values + if feedback[0].Name == "status" { + feedbackRaw := *feedback[0].Value.JsonRaw + if !strings.Contains(feedbackRaw, "error looking up service account default/nginx") { + return nil + } + } + } - return nil - }, 3*time.Minute, 3*time.Second).ShouldNot(HaveOccurred()) + + return fmt.Errorf("unexpected status") + }, 2*time.Minute, 2*time.Second).ShouldNot(HaveOccurred()) }) It("delete the nginx resource", func() { - resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() + err := sourceWorkClient.ManifestWorks(agentTestOpts.consumerName).Delete(ctx, workName, metav1.DeleteOptions{}) Expect(err).ShouldNot(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) Eventually(func() error { - _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, name, metav1.GetOptions{}) + _, err := agentTestOpts.kubeClientSet.CoreV1().Secrets("default").Get(ctx, deployName, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { return nil @@ -173,190 +174,3 @@ var _ = Describe("Status Resync After Restart", Ordered, Label("e2e-tests-status }) }) }) - -// var _ = Describe("Status Resync After Reconnect", Ordered, Label("e2e-tests-status-resync-reconnect"), func() { -// Context("Resource resync resource status after maestro server reconnects", func() { -// var mqttReplicas int -// var resource *openapi.Resource -// name := fmt.Sprintf("nginx-%s", rand.String(5)) -// It("post the nginx resource with non-default service account to the maestro api", func() { -// res := helper.NewAPIResourceWithSA(agentTestOpts.consumerName, name, name, 1) -// var resp *http.Response -// var err error -// resource, resp, err = apiClient.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusCreated)) -// Expect(*resource.Id).ShouldNot(BeEmpty()) - -// Eventually(func() error { -// deploy, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, name, metav1.GetOptions{}) -// if err != nil { -// return err -// } -// if *deploy.Spec.Replicas != 1 { -// return fmt.Errorf("unexpected replicas for nginx deployment %s, expected 1, got %d", name, *deploy.Spec.Replicas) -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - -// gotResource, resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusOK)) -// Expect(*gotResource.Id).To(Equal(*resource.Id)) -// Expect(*gotResource.Version).To(Equal(*resource.Version)) - -// Eventually(func() error { -// gotResource, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() -// if err != nil { -// return err -// } -// statusJSON, err := json.Marshal(gotResource.Status) -// if err != nil { -// return err -// } -// if !strings.Contains(string(statusJSON), "error looking up service account default/nginx") { -// return fmt.Errorf("unexpected status, expected error looking up service account default/nginx, got %s", string(statusJSON)) -// } -// return nil -// }, 2*time.Minute, 2*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("delete the mqtt-broker service for server", func() { -// err := serverTestOpts.kubeClientSet.CoreV1().Services(serverTestOpts.serverNamespace).Delete(ctx, "maestro-mqtt-server", metav1.DeleteOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("create serviceaccount for nginx deployment", func() { -// _, err := agentTestOpts.kubeClientSet.CoreV1().ServiceAccounts("default").Create(ctx, &corev1.ServiceAccount{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: name, -// }, -// }, metav1.CreateOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) - -// // delete the nginx deployment to tigger recreating -// err = agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Delete(ctx, name, metav1.DeleteOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("Rollout the mqtt-broker", func() { -// deploy, err := serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Get(ctx, "maestro-mqtt", metav1.GetOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// mqttReplicas = int(*deploy.Spec.Replicas) -// deploy, err = serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Patch(ctx, "maestro-mqtt", types.MergePatchType, []byte(`{"spec":{"replicas":0}}`), metav1.PatchOptions{ -// FieldManager: "serverTestOpts.kubeClientSet", -// }) -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(*deploy.Spec.Replicas).To(Equal(int32(0))) - -// // ensure no running mqtt-broker pods -// Eventually(func() error { -// pods, err := serverTestOpts.kubeClientSet.CoreV1().Pods(serverTestOpts.serverNamespace).List(ctx, metav1.ListOptions{ -// LabelSelector: "name=maestro-mqtt", -// }) -// if err != nil { -// return err -// } -// if len(pods.Items) > 0 { -// return fmt.Errorf("maestro-mqtt pods still running") -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) - -// // patch mqtt-broker replicas to mqttReplicas -// deploy, err = serverTestOpts.kubeClientSet.AppsV1().Deployments(serverTestOpts.serverNamespace).Patch(ctx, "maestro-mqtt", types.MergePatchType, []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, mqttReplicas)), metav1.PatchOptions{ -// FieldManager: "serverTestOpts.kubeClientSet", -// }) -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(*deploy.Spec.Replicas).To(Equal(int32(mqttReplicas))) - -// // ensure mqtt-broker pod is up and running -// Eventually(func() error { -// pods, err := serverTestOpts.kubeClientSet.CoreV1().Pods(serverTestOpts.serverNamespace).List(ctx, metav1.ListOptions{ -// LabelSelector: "name=maestro-mqtt", -// }) -// if err != nil { -// return err -// } -// if len(pods.Items) != mqttReplicas { -// return fmt.Errorf("unexpected maestro-mqtt pod count, expected %d, got %d", mqttReplicas, len(pods.Items)) -// } -// for _, pod := range pods.Items { -// if pod.Status.Phase != "Running" { -// return fmt.Errorf("maestro-mqtt pod not in running state") -// } -// if pod.Status.ContainerStatuses[0].State.Running == nil { -// return fmt.Errorf("maestro-mqtt container not in running state") -// } -// } -// return nil -// }, 1*time.Minute, 1*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("recreate the mqtt-broker service for server", func() { -// mqttServerService := &corev1.Service{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "maestro-mqtt-server", -// Namespace: serverTestOpts.serverNamespace, -// }, -// Spec: corev1.ServiceSpec{ -// Selector: map[string]string{ -// "name": "maestro-mqtt", -// }, -// Ports: []corev1.ServicePort{ -// { -// Name: "mosquitto", -// Protocol: corev1.ProtocolTCP, -// Port: 1883, -// TargetPort: intstr.FromInt(1883), -// }, -// }, -// Type: corev1.ServiceTypeClusterIP, -// }, -// } - -// _, err := serverTestOpts.kubeClientSet.CoreV1().Services(serverTestOpts.serverNamespace).Create(ctx, mqttServerService, metav1.CreateOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) - -// It("ensure the resource status is resynced", func() { -// Eventually(func() error { -// gotResource, _, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() -// if err != nil { -// return err -// } -// if _, ok := gotResource.Status["ContentStatus"]; !ok { -// return fmt.Errorf("unexpected status, expected contains ContentStatus, got %v", gotResource.Status) -// } -// statusJSON, err := json.Marshal(gotResource.Status) -// if err != nil { -// return err -// } -// if strings.Contains(string(statusJSON), "error looking up service account default/nginx") { -// return fmt.Errorf("unexpected status, should not contain error looking up service account default/nginx, got %s", string(statusJSON)) -// } -// return nil -// }, 3*time.Minute, 3*time.Second).ShouldNot(HaveOccurred()) -// }) - -// It("delete the nginx resource", func() { -// resp, err := apiClient.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() -// Expect(err).ShouldNot(HaveOccurred()) -// Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) - -// Eventually(func() error { -// _, err := agentTestOpts.kubeClientSet.AppsV1().Deployments("default").Get(ctx, name, metav1.GetOptions{}) -// if err != nil { -// if errors.IsNotFound(err) { -// return nil -// } -// return err -// } -// return fmt.Errorf("nginx deployment still exists") -// }, 2*time.Minute, 2*time.Second).ShouldNot(HaveOccurred()) - -// err = agentTestOpts.kubeClientSet.CoreV1().ServiceAccounts("default").Delete(ctx, name, metav1.DeleteOptions{}) -// Expect(err).ShouldNot(HaveOccurred()) -// }) -// }) -// }) diff --git a/test/factories.go b/test/factories.go index 5c34a03f..ac4fbdb1 100755 --- a/test/factories.go +++ b/test/factories.go @@ -11,7 +11,6 @@ import ( cloudevents "github.com/cloudevents/sdk-go/v2" "github.com/openshift-online/maestro/pkg/api" - "github.com/openshift-online/maestro/pkg/api/openapi" "github.com/openshift-online/maestro/pkg/db" "golang.org/x/oauth2" "google.golang.org/grpc" @@ -31,7 +30,7 @@ import ( workpayload "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" ) -var testManifestJSON = ` +var manifestJSON = ` { "apiVersion": "apps/v1", "kind": "Deployment", @@ -66,123 +65,15 @@ var testManifestJSON = ` } ` -var testReadOnlyManifestJSON = ` -{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "%s", - "namespace": "%s" - } -} -` - -// NewAPIResource creates an API resource with the given consumer name, deploy name, and replicas. -// It generates a deployment for nginx using the testManifestJSON template, giving it a random deploy -// name to avoid testing conflicts. -func (helper *Helper) NewAPIResource(consumerName, deployName string, replicas int) openapi.Resource { - sa := "default" // default service account - return helper.NewAPIResourceWithSA(consumerName, deployName, sa, replicas) -} - -// NewAPIResourceWithSA creates an API resource with the given consumer name, deploy name, service account, and replicas. -// It generates a nginx deployment using the testManifestJSON template, assigning a random deploy name to avoid testing conflicts. -func (helper *Helper) NewAPIResourceWithSA(consumerName, deployName, sa string, replicas int) openapi.Resource { - namespace := "default" // default namespace - testManifest := map[string]interface{}{} - if err := json.Unmarshal([]byte(fmt.Sprintf(testManifestJSON, deployName, namespace, replicas, sa)), &testManifest); err != nil { - helper.T.Errorf("error unmarshalling manifest: %q", err) - } - - return openapi.Resource{ - ConsumerName: &consumerName, - Manifest: testManifest, - GroupResource: map[string]interface{}{ - "group": "apps", - "resource": "deployments", - }, - } -} - // NewResourceManifestJSON creates a resource manifest in JSON format with the given deploy name and replicas. -// It generates a deployment for nginx using the testManifestJSON template, assigning a random deploy name to avoid -// testing conflicts. -func (helper *Helper) NewResourceManifestJSON(deployName string, replicas int) string { +// It generates a deployment for nginx using the manifestJSON template, assigning a random deploy name to avoid conflicts. +func (helper *Helper) NewResourceManifestJSON(deployName, serviceAccount string, replicas int) string { namespace := "default" // default namespace - sa := "default" // default service account - return fmt.Sprintf(testManifestJSON, deployName, namespace, replicas, sa) + return fmt.Sprintf(manifestJSON, deployName, namespace, replicas, serviceAccount) } -// NewReadOnlyAPIResource creates an API resource with the given consumer name and deploy name. -// It generates a read-only deployment manifests for nginx using the testReadOnlyManifestJSON template, -// giving it a random deploy name to avoid testing conflicts. -func (helper *Helper) NewReadOnlyAPIResource(consumerName, deployName string) openapi.Resource { - namespace := "default" // default namespace - testManifest := map[string]interface{}{} - if err := json.Unmarshal([]byte(fmt.Sprintf(testReadOnlyManifestJSON, deployName, namespace)), &testManifest); err != nil { - helper.T.Errorf("error unmarshalling test manifest: %q", err) - } - - return openapi.Resource{ - Manifest: testManifest, - ConsumerName: &consumerName, - GroupResource: map[string]interface{}{ - "group": "apps", - "resource": "deployments", - }, - UpdateStrategy: map[string]interface{}{ - "type": "ReadOnly", - }, - } -} - -// NewReadOnlyResourceManifestJSON creates a resource with the given consumer name, deploy name, replicas, and resource version. -// It generates a deployment for nginx using the testManifestJSON template, assigning a random deploy name to avoid testing conflicts. -func (helper *Helper) NewResource(consumerName, deployName string, replicas int, resourceVersion int32) *api.Resource { - testResource := helper.NewAPIResource(consumerName, deployName, replicas) - testPayload, err := api.EncodeManifest(testResource.Manifest, testResource.GroupResource, testResource.DeleteOption, testResource.UpdateStrategy) - if err != nil { - helper.T.Errorf("error encoding manifest: %q", err) - } - - resource := &api.Resource{ - ConsumerName: consumerName, - Type: api.ResourceTypeSingle, - Payload: testPayload, - Version: resourceVersion, - } - - return resource -} - -// CreateResource creates a resource with the given consumer name, deploy name, and replicas. -// It generates a deployment for nginx using the testManifestJSON template, assigning a random deploy name to avoid testing conflicts. -func (helper *Helper) CreateResource(consumerName, deployName string, replicas int) *api.Resource { - resource := helper.NewResource(consumerName, deployName, replicas, 1) - resourceService := helper.Env().Services.Resources() - - res, err := resourceService.Create(context.Background(), resource) - if err != nil { - helper.T.Errorf("error creating resource: %q", err) - } - - return res -} - -// CreateResourceList generates a list of resources with the specified consumer name and count. -// Each resource gets a randomly generated deploy name for nginx deployments to avoid testing conflicts. -func (helper *Helper) CreateResourceList(consumerName string, count int) (resources []*api.Resource) { - for i := 1; i <= count; i++ { - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - resources = append(resources, helper.CreateResource(consumerName, deployName, 1)) - time.Sleep(10 * time.Millisecond) - } - - return resources -} - -// EncodeManifestBundle converts resource manifest JSON into a CloudEvent JSONMap representation. -func (helper *Helper) EncodeManifestBundle(manifestJSON, deployName, deployNamespace string) (datatypes.JSONMap, error) { +// EncodeManifest converts resource manifest JSON into a CloudEvent JSONMap representation. +func (helper *Helper) EncodeManifest(manifestJSON, deployName, deployNamespace string) (datatypes.JSONMap, error) { if len(manifestJSON) == 0 { return nil, nil } @@ -245,89 +136,176 @@ func (helper *Helper) EncodeManifestBundle(manifestJSON, deployName, deployNames } // convert cloudevent to JSONMap - manifestBundle, err := api.CloudEventToJSONMap(&evt) + manifest, err := api.CloudEventToJSONMap(&evt) if err != nil { return nil, fmt.Errorf("failed to convert cloudevent to resource manifest: %v", err) } - return manifestBundle, nil + return manifest, nil } -// NewResourceBundle creates a resource bundle with the given consumer name, deploy name, replicas, and resource version. -func (helper *Helper) NewResourceBundle(consumerName, deployName string, replicas int, resourceVersion int32) *api.Resource { +// NewResource creates a resource with the given consumer name, deploy name, replicas, and resource version. +func (helper *Helper) NewResource(consumerName, deployName, serviceAccount string, replicas int, resourceVersion int32) (*api.Resource, error) { namespace := "default" // default namespace - manifestJSON := helper.NewResourceManifestJSON(deployName, replicas) - payload, err := helper.EncodeManifestBundle(manifestJSON, deployName, namespace) + manifestJSON := helper.NewResourceManifestJSON(deployName, serviceAccount, replicas) + payload, err := helper.EncodeManifest(manifestJSON, deployName, namespace) if err != nil { - helper.T.Errorf("error encoding manifest bundle: %q", err) + return nil, err } resource := &api.Resource{ ConsumerName: consumerName, - Type: api.ResourceTypeBundle, Payload: payload, Version: resourceVersion, } - return resource + return resource, nil } -// CreateResourceBundle creates a resource bundle with the given consumer name, deploy name and replicas. -// It generates a deployment for nginx using the testManifestJSON template, assigning a random deploy name to avoid testing conflicts. -func (helper *Helper) CreateResourceBundle(consumerName, deployName string, replicas int) *api.Resource { - resourceBundle := helper.NewResourceBundle(consumerName, deployName, replicas, 1) +// CreateResource creates a resource with the given consumer name, deploy name and replicas. +// It generates a deployment for nginx using the manifestJSON template, assigning a random deploy name to avoid conflicts. +func (helper *Helper) CreateResource(consumerName, deployName, serviceAccount string, replicas int) (*api.Resource, error) { + resource, err := helper.NewResource(consumerName, deployName, serviceAccount, replicas, 1) + if err != nil { + return nil, err + } resourceService := helper.Env().Services.Resources() + res, svcErr := resourceService.Create(context.Background(), resource) + if svcErr != nil { + return nil, svcErr.AsError() + } - res, err := resourceService.Create(context.Background(), resourceBundle) + return res, nil +} + +// UpdateResource attempts to update a resource, resource ID must not be empty. +func (helper *Helper) UpdateResource(resource *api.Resource) (*api.Resource, error) { + resourceService := helper.Env().Services.Resources() + res, err := resourceService.Update(context.Background(), resource) if err != nil { - helper.T.Errorf("error creating resource bundle: %q", err) + return nil, err.AsError() } - return res + return res, nil } -// CreateResourceBundleList generates a list of resource bundles with the specified consumer name and count. -// Each resource gets a randomly generated deploy name for nginx deployments to avoid testing conflicts. -func (helper *Helper) CreateResourceBundleList(consumerName string, count int) (resourceBundles []*api.Resource) { - for i := 1; i <= count; i++ { +// CreateResourceList generates a list of resources with the specified consumer name and count. +// Each resource gets a randomly generated deploy name for nginx deployments to avoid conflicts. +func (helper *Helper) CreateResourceList(consumerName string, count int) ([]*api.Resource, error) { + resources := make([]*api.Resource, count) + for i := 0; i < count; i++ { deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - resourceBundles = append(resourceBundles, helper.CreateResourceBundle(consumerName, deployName, 1)) + resource, err := helper.CreateResource(consumerName, deployName, "default", 1) + if err != nil { + return resources, err + } + resources[i] = resource + } + + return resources, nil +} + +// DeleteResource attempts to delete a resource and returns an error if it fails. +func (helper *Helper) DeleteResource(id string) error { + resourceService := helper.Env().Services.Resources() + if err := resourceService.MarkAsDeleting(context.Background(), id); err != nil { + return err.AsError() + } + + return nil +} + +// NewManifest creats a manifest with the given deploy name and replicas. +// It generates a deployment for nginx using the manifestJSON template, assigning random +// deploy name to avoid conflicts. +func (helper *Helper) NewManifest(deployName, serviceAccount string, replicas int) workv1.Manifest { + manifestJSON := helper.NewResourceManifestJSON(deployName, serviceAccount, replicas) + return workv1.Manifest{ + RawExtension: runtime.RawExtension{ + Raw: []byte(manifestJSON), + }, + } +} + +// NewManifestWork creates a manifestwork with the given manifestwork name, deploy name and replicas. +// It generates a deployment for nginx using the manifestJSON template, assigning random manifestwork name +// and deploy name to avoid conflicts. +func (helper *Helper) NewManifestWork(workName, deployName, serviceAccount string, replicas int) *workv1.ManifestWork { + manifest := helper.NewManifest(deployName, serviceAccount, replicas) + return &workv1.ManifestWork{ + ObjectMeta: metav1.ObjectMeta{ + Name: workName, + }, + Spec: workv1.ManifestWorkSpec{ + Workload: workv1.ManifestsTemplate{ + Manifests: []workv1.Manifest{manifest}, + }, + ManifestConfigs: []workv1.ManifestConfigOption{ + { + ResourceIdentifier: workv1.ResourceIdentifier{ + Group: "apps", + Resource: "deployments", + Name: deployName, + Namespace: "default", + }, + FeedbackRules: []workv1.FeedbackRule{ + { + Type: workv1.JSONPathsType, + JsonPaths: []workv1.JsonPath{ + { + Name: "status", + Path: ".status", + }, + }, + }, + }, + UpdateStrategy: &workv1.UpdateStrategy{ + Type: workv1.UpdateStrategyTypeServerSideApply, + }, + }, + }, + }, } - return resourceBundles } -func (helper *Helper) CreateConsumer(name string) *api.Consumer { +func (helper *Helper) CreateConsumer(name string) (*api.Consumer, error) { return helper.CreateConsumerWithLabels(name, nil) } -func (helper *Helper) CreateConsumerWithLabels(name string, labels map[string]string) *api.Consumer { +func (helper *Helper) CreateConsumerWithLabels(name string, labels map[string]string) (*api.Consumer, error) { consumerService := helper.Env().Services.Consumers() consumer, err := consumerService.Create(context.Background(), &api.Consumer{Name: name, Labels: db.EmptyMapToNilStringMap(&labels)}) if err != nil { - helper.T.Errorf("error creating consumer: %q", err) + return nil, err } - return consumer + + return consumer, nil } -func (helper *Helper) CreateConsumerList(count int) (consumers []*api.Consumer) { - for i := 1; i <= count; i++ { - consumers = append(consumers, helper.CreateConsumer(fmt.Sprintf("consumer-%d", i))) +func (helper *Helper) CreateConsumerList(count int) ([]*api.Consumer, error) { + consumers := make([]*api.Consumer, count) + for i := 0; i < count; i++ { + consumer, err := helper.CreateConsumer(fmt.Sprintf("consumer-%d", i)) + if err != nil { + return consumers, err + } + consumers[i] = consumer } - return consumers + return consumers, nil } // NewEvent creates a CloudEvent with the given source, action, consumer name, resource ID, deployment name, resource version, and replicas. -// It generates a nginx deployment using the testManifestJSON template, assigning a random deploy name to avoid testing conflicts. +// It generates a nginx deployment using the manifestJSON template, assigning a random deploy name to avoid conflicts. // If the action is "delete_request," the event includes a deletion timestamp. -func (helper *Helper) NewEvent(source, action, consumerName, resourceID, deployName string, resourceVersion int64, replicas int) *cloudevents.Event { +func (helper *Helper) NewEvent(source, action, consumerName, resourceID, deployName string, resourceVersion int64, replicas int) (*cloudevents.Event, error) { sa := "default" // default service account deployNamespace := "default" // default namespace - testManifest := map[string]interface{}{} - if err := json.Unmarshal([]byte(fmt.Sprintf(testManifestJSON, deployName, deployNamespace, replicas, sa)), &testManifest); err != nil { - helper.T.Errorf("error unmarshalling manifest: %q", err) + manifest := map[string]interface{}{} + if err := json.Unmarshal([]byte(fmt.Sprintf(manifestJSON, deployName, deployNamespace, replicas, sa)), &manifest); err != nil { + return nil, err } eventType := cetypes.CloudEventsType{ @@ -352,14 +330,14 @@ func (helper *Helper) NewEvent(source, action, consumerName, resourceID, deployN // if action is delete_request, no data is needed if action == "delete_request" { evt.SetData(cloudevents.ApplicationJSON, nil) - return &evt + return &evt, nil } eventPayload := &workpayload.ManifestBundle{ Manifests: []workv1.Manifest{ { RawExtension: runtime.RawExtension{ - Object: &unstructured.Unstructured{Object: testManifest}, + Object: &unstructured.Unstructured{Object: manifest}, }, }, }, @@ -393,10 +371,10 @@ func (helper *Helper) NewEvent(source, action, consumerName, resourceID, deployN } if err := evt.SetData(cloudevents.ApplicationJSON, eventPayload); err != nil { - helper.T.Errorf("failed to set cloud event data: %q", err) + return nil, err } - return &evt + return &evt, nil } func (helper *Helper) CreateGRPCAuthRule(ctx context.Context, kubeClient kubernetes.Interface, ruleName, resourceType, resourceID string, actions []string) error { diff --git a/test/grpc_codec.go b/test/grpc_codec.go deleted file mode 100644 index a38e11e2..00000000 --- a/test/grpc_codec.go +++ /dev/null @@ -1,135 +0,0 @@ -package test - -import ( - "encoding/json" - "fmt" - - cloudevents "github.com/cloudevents/sdk-go/v2" - cloudeventstypes "github.com/cloudevents/sdk-go/v2/types" - "github.com/openshift-online/maestro/pkg/api" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - workv1 "open-cluster-management.io/api/work/v1" - "open-cluster-management.io/sdk-go/pkg/cloudevents/generic" - "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/types" - "open-cluster-management.io/sdk-go/pkg/cloudevents/work/common" - "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" -) - -type ResourceCodec struct{} - -var _ generic.Codec[*api.Resource] = &ResourceCodec{} - -func (c *ResourceCodec) EventDataType() types.CloudEventsDataType { - return payload.ManifestBundleEventDataType -} - -// encode the kubernetes resource to a cloudevent format -func (c *ResourceCodec) Encode(source string, eventType types.CloudEventsType, resource *api.Resource) (*cloudevents.Event, error) { - if eventType.CloudEventsDataType != payload.ManifestBundleEventDataType { - return nil, fmt.Errorf("unsupported cloudevents data type %s", eventType.CloudEventsDataType) - } - - eventBuilder := types.NewEventBuilder(source, eventType). - WithResourceID(resource.ID). - WithResourceVersion(int64(resource.Version)). - WithClusterName(resource.ConsumerName) - - if !resource.GetDeletionTimestamp().IsZero() { - evt := eventBuilder.WithDeletionTimestamp(resource.GetDeletionTimestamp().Time).NewEvent() - return &evt, nil - } - - manifest, _, _, _, err := api.DecodeManifest(resource.Payload) - if err != nil { - return nil, fmt.Errorf("failed to decode manifest: %v", err) - } - - evt := eventBuilder.NewEvent() - - manifests := &payload.ManifestBundle{ - Manifests: []workv1.Manifest{ - { - RawExtension: runtime.RawExtension{ - Object: &unstructured.Unstructured{Object: manifest}, - }, - }, - }, - DeleteOption: &workv1.DeleteOption{ - PropagationPolicy: workv1.DeletePropagationPolicyTypeForeground, - }, - ManifestConfigs: []workv1.ManifestConfigOption{}, - } - if err := evt.SetData(cloudevents.ApplicationJSON, manifests); err != nil { - return nil, fmt.Errorf("failed to encode resource bundle to a cloudevent: %v", err) - } - - return &evt, nil -} - -func (c *ResourceCodec) Decode(evt *cloudevents.Event) (*api.Resource, error) { - eventType, err := types.ParseCloudEventsType(evt.Type()) - if err != nil { - return nil, fmt.Errorf("failed to parse cloud event type %s, %v", evt.Type(), err) - } - - if eventType.CloudEventsDataType != payload.ManifestBundleEventDataType { - return nil, fmt.Errorf("unsupported cloudevents data type %s", eventType.CloudEventsDataType) - } - - evtExtensions := evt.Context.GetExtensions() - - resourceID, err := cloudeventstypes.ToString(evtExtensions[types.ExtensionResourceID]) - if err != nil { - return nil, fmt.Errorf("failed to get resourceid extension: %v", err) - } - - resourceVersion, err := cloudeventstypes.ToInteger(evtExtensions[types.ExtensionResourceVersion]) - if err != nil { - return nil, fmt.Errorf("failed to get resourceversion extension: %v", err) - } - - clusterName, err := cloudeventstypes.ToString(evtExtensions[types.ExtensionClusterName]) - if err != nil { - return nil, fmt.Errorf("failed to get clustername extension: %v", err) - } - - manifestStatus := &payload.ManifestBundleStatus{} - if err := evt.DataAs(manifestStatus); err != nil { - return nil, fmt.Errorf("failed to unmarshal event data %s, %v", string(evt.Data()), err) - } - - resource := &api.Resource{ - Meta: api.Meta{ - ID: resourceID, - }, - Version: resourceVersion, - ConsumerName: clusterName, - } - - resourceStatus := &api.ResourceStatus{ - ReconcileStatus: &api.ReconcileStatus{ - ObservedVersion: resourceVersion, - }, - } - - if len(manifestStatus.ResourceStatus) > 0 { - resourceStatus.ReconcileStatus.Conditions = manifestStatus.ResourceStatus[0].Conditions - if meta.IsStatusConditionTrue(manifestStatus.Conditions, common.ManifestsDeleted) { - deletedCondition := meta.FindStatusCondition(manifestStatus.Conditions, common.ManifestsDeleted) - resourceStatus.ReconcileStatus.Conditions = append(resourceStatus.ReconcileStatus.Conditions, *deletedCondition) - } - } - - resourceStatusJSON, err := json.Marshal(resourceStatus) - if err != nil { - return nil, fmt.Errorf("failed to marshal resource status: %v", err) - } - err = json.Unmarshal(resourceStatusJSON, &resource.Status) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal resource status: %v", err) - } - - return resource, nil -} diff --git a/test/helper.go b/test/helper.go index 76b718c2..f6e0a55f 100755 --- a/test/helper.go +++ b/test/helper.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/openshift-online/maestro/pkg/client/cloudevents" "github.com/openshift-online/maestro/pkg/controllers" "github.com/openshift-online/maestro/pkg/dao" "github.com/openshift-online/maestro/pkg/dispatcher" @@ -327,15 +328,16 @@ func (helper *Helper) StartWorkAgent(ctx context.Context, clusterName string) { } func (helper *Helper) StartGRPCResourceSourceClient() { + source := "maestro" store := NewStore() grpcOptions := grpcoptions.NewGRPCOptions() grpcOptions.URL = fmt.Sprintf("%s:%s", helper.Env().Config.HTTPServer.Hostname, helper.Env().Config.GRPCServer.ServerBindPort) sourceClient, err := generic.NewCloudEventSourceClient[*api.Resource]( helper.Ctx, - grpcoptions.NewSourceOptions(grpcOptions, "maestro"), + grpcoptions.NewSourceOptions(grpcOptions, source), store, resourceStatusHashGetter, - &ResourceCodec{}, + cloudevents.NewCodec(source), ) if err != nil { diff --git a/test/integration/consumers_test.go b/test/integration/consumers_test.go index 284da9a1..5ef6c21d 100644 --- a/test/integration/consumers_test.go +++ b/test/integration/consumers_test.go @@ -11,6 +11,7 @@ import ( "gopkg.in/resty.v1" "k8s.io/apimachinery/pkg/util/rand" + "github.com/openshift-online/maestro/pkg/api" "github.com/openshift-online/maestro/pkg/api/openapi" "github.com/openshift-online/maestro/test" ) @@ -30,7 +31,8 @@ func TestConsumerGet(t *testing.T) { Expect(err).To(HaveOccurred(), "Expected 404") Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) - consumer := h.CreateConsumerWithLabels("cluster-"+rand.String(5), map[string]string{"foo": "bar"}) + consumer, err := h.CreateConsumerWithLabels("cluster-"+rand.String(5), map[string]string{"foo": "bar"}) + Expect(err).NotTo(HaveOccurred()) found, resp, err := client.DefaultApi.ApiMaestroV1ConsumersIdGet(ctx, consumer.ID).Execute() Expect(err).NotTo(HaveOccurred()) @@ -102,7 +104,8 @@ func TestConsumerPatch(t *testing.T) { ctx := h.NewAuthenticatedContext(account) // create a consumer - consumer := h.CreateConsumer("brontosaurus") + consumer, err := h.CreateConsumer("brontosaurus") + Expect(err).NotTo(HaveOccurred()) assert := func(patched *openapi.Consumer, resp *http.Response, err error, name *string, labels *map[string]string) { Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) @@ -189,12 +192,9 @@ func TestConsumerDeleteForbidden(t *testing.T) { // attach resource to the consumer deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.NewAPIResource(*consumer.Name, deployName, 1) - resource, resp, err := client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).To(Succeed()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).ShouldNot(BeEmpty()) - Expect(*resource.Version).To(Equal(int32(1))) + res, err := h.CreateResource(*consumer.Name, deployName, "default", 1) + Expect(err).NotTo(HaveOccurred()) + Expect(res.ID).ShouldNot(BeEmpty()) // 403 forbid deletion resp, err = client.DefaultApi.ApiMaestroV1ConsumersIdDelete(ctx, *consumer.Id).Execute() @@ -202,9 +202,8 @@ func TestConsumerDeleteForbidden(t *testing.T) { Expect(resp.StatusCode).To(Equal(http.StatusForbidden)) // delete the resource - resp, err = client.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, *resource.Id).Execute() - Expect(err).To(Succeed()) - Expect(resp.StatusCode).To(Equal(http.StatusNoContent)) + err = h.DeleteResource(res.ID) + Expect(err).NotTo(HaveOccurred()) // still forbid deletion for the deleting resource resp, err = client.DefaultApi.ApiMaestroV1ConsumersIdDelete(ctx, *consumer.Id).Execute() @@ -225,7 +224,7 @@ func TestConsumerDeleting(t *testing.T) { consumer, resp, err := client.DefaultApi.ApiMaestroV1ConsumersPost(ctx).Consumer(openapi.Consumer{ Name: openapi.PtrString(consumerName), }).Execute() - Expect(err).To(Succeed()) + Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusCreated)) Expect(*consumer.Id).NotTo(BeEmpty()) consumerIdToName[*consumer.Id] = consumerName @@ -247,11 +246,9 @@ func TestConsumerDeleting(t *testing.T) { defer wg.Done() for i := 0; i < resourceNum; i++ { deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.NewAPIResource(name, deployName, 1) - resource, resp, err := client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() + res, err := h.CreateResource(name, deployName, "default", 1) resourceChan <- &Result{ - resource: resource, - resp: resp, + resource: res, consumerName: name, consumerId: id, err: err, @@ -297,7 +294,7 @@ func TestConsumerDeleting(t *testing.T) { Expect(resourceList.Items).To(BeEmpty()) } else { // at least one resource on the consumer, the statusCode should be 403 or 500 - fmt.Printf("failed to delete consumer(%s), associated with resource(%d), statusCode: %d, err: %v \n", consumerName, len(resourceList.Items), consumerStatusCode, consumerErr) + fmt.Printf("failed to delete consumer(%s), associated with resource(%d), statusCode: %d, err: %v\n", consumerName, len(resourceList.Items), consumerStatusCode, consumerErr) Expect(resourceList.Items).NotTo(BeEmpty(), resourceList.Items) } } @@ -309,22 +306,23 @@ func TestConsumerDeleting(t *testing.T) { for i := 0; i < consumerNum*resourceNum*resourceCreatorNum; i++ { result := <-resourceChan - resourceStatusCode := result.resp.StatusCode + resource := result.resource + resourceErr := result.err resourceConsumerId := result.consumerId resourceConsumerName := result.consumerName // get the consumer consumer, resp, err := client.DefaultApi.ApiMaestroV1ConsumersIdGet(ctx, resourceConsumerId).Execute() - if resourceStatusCode == http.StatusCreated { - Expect(err).To(Succeed()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*consumer.Id).To(Equal(resourceConsumerId)) - Expect(*consumer.Name).To(Equal(resourceConsumerName)) - } else { - fmt.Printf("failed to create resource on consumer(%s), statusCode: %d, err: %v \n", resourceConsumerName, resourceStatusCode, result.err) + if resourceErr != nil { + fmt.Printf("failed to create resource on consumer(%s): %v\n", resourceConsumerName, result.err) Expect(err.Error()).To(ContainSubstring("Not Found")) Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + } else { + Expect(resource).NotTo(BeNil()) + Expect(err).NotTo(HaveOccurred()) + Expect(*consumer.Id).To(Equal(resourceConsumerId)) + Expect(*consumer.Name).To(Equal(resourceConsumerName)) } } close(resourceChan) @@ -337,7 +335,8 @@ func TestConsumerPaging(t *testing.T) { ctx := h.NewAuthenticatedContext(account) // Paging - _ = h.CreateConsumerList(20) + _, err := h.CreateConsumerList(20) + Expect(err).NotTo(HaveOccurred()) list, _, err := client.DefaultApi.ApiMaestroV1ConsumersGet(ctx).Execute() Expect(err).NotTo(HaveOccurred(), "Error getting consumer list: %v", err) @@ -357,7 +356,7 @@ func TestConsumerPaging(t *testing.T) { } type Result struct { - resource *openapi.Resource + resource *api.Resource consumerName string consumerId string resp *http.Response diff --git a/test/integration/controller_test.go b/test/integration/controller_test.go index e23ff156..918643b2 100755 --- a/test/integration/controller_test.go +++ b/test/integration/controller_test.go @@ -27,7 +27,8 @@ func TestControllerRacing(t *testing.T) { }() // start work agent so that grpc broker can work - consumer := h.CreateConsumer("cluster-" + rand.String(5)) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) h.StartWorkAgent(ctx, consumer.Name) eventDao := dao.NewEventDao(&h.Env().Database.SessionFactory) @@ -122,7 +123,8 @@ func TestControllerRacing(t *testing.T) { // wait for controller service starts time.Sleep(3 * time.Second) - resources := h.CreateResourceList(consumer.Name, 50) + resources, err := h.CreateResourceList(consumer.Name, 50) + Expect(err).NotTo(HaveOccurred()) // This is to check only 50 create events are processed. It waits for 5 seconds to ensure all events have been // processed by the controllers. @@ -162,7 +164,8 @@ func TestControllerReconcile(t *testing.T) { ctx, cancel := context.WithCancel(h.NewAuthenticatedContext(account)) // start work agent so that grpc broker can work - consumer := h.CreateConsumer("cluster-" + rand.String(5)) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) h.StartWorkAgent(ctx, consumer.Name) eventDao := dao.NewEventDao(&h.Env().Database.SessionFactory) @@ -224,7 +227,8 @@ func TestControllerReconcile(t *testing.T) { time.Sleep(time.Second) deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - resource := h.CreateResource(consumer.Name, deployName, 1) + resource, err := h.CreateResource(consumer.Name, deployName, "default", 1) + Expect(err).NotTo(HaveOccurred()) // Eventually, the event will be processed by the controller. Eventually(func() error { @@ -284,7 +288,8 @@ func TestControllerSync(t *testing.T) { ctx, cancel := context.WithCancel(h.NewAuthenticatedContext(account)) // start work agent so that grpc broker can work - consumer := h.CreateConsumer("cluster-" + rand.String(5)) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) h.StartWorkAgent(ctx, consumer.Name) // create two resources with resource dao diff --git a/test/integration/resource_test.go b/test/integration/resource_test.go index d0afbc06..01aa1f3c 100755 --- a/test/integration/resource_test.go +++ b/test/integration/resource_test.go @@ -16,7 +16,6 @@ import ( . "github.com/onsi/gomega" prommodel "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" - "gopkg.in/resty.v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8stypes "k8s.io/apimachinery/pkg/types" @@ -28,8 +27,8 @@ import ( "open-cluster-management.io/sdk-go/pkg/cloudevents/work/payload" "github.com/openshift-online/maestro/pkg/api" - "github.com/openshift-online/maestro/pkg/api/openapi" "github.com/openshift-online/maestro/pkg/dao" + "github.com/openshift-online/maestro/pkg/errors" "github.com/openshift-online/maestro/test" ) @@ -48,360 +47,31 @@ func TestResourceGet(t *testing.T) { Expect(err).To(HaveOccurred(), "Expected 404") Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) - consumer := h.CreateConsumer("cluster-" + rand.String(5)) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - resource := h.CreateResource(consumer.Name, deployName, 1) + resource, err := h.CreateResource(consumer.Name, deployName, "default", 1) + Expect(err).NotTo(HaveOccurred()) res, resp, err := client.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, resource.ID).Execute() Expect(err).NotTo(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) Expect(*res.Id).To(Equal(resource.ID), "found object does not match test object") + Expect(*res.Name).To(Equal(resource.Name)) Expect(*res.Kind).To(Equal("Resource")) Expect(*res.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resources/%s", resource.ID))) Expect(*res.CreatedAt).To(BeTemporally("~", resource.CreatedAt)) Expect(*res.UpdatedAt).To(BeTemporally("~", resource.UpdatedAt)) Expect(*res.Version).To(Equal(resource.Version)) -} - -func TestResourcePost(t *testing.T) { - h, client := test.RegisterIntegration(t) - account := h.NewRandAccount() - ctx, cancel := context.WithCancel(h.NewAuthenticatedContext(account)) - defer func() { - cancel() - }() - - clusterName := "cluster-" + rand.String(5) - consumer := h.CreateConsumer(clusterName) - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.NewAPIResource(consumer.Name, deployName, 1) - h.StartControllerManager(ctx) - h.StartWorkAgent(ctx, consumer.Name) - clientHolder := h.WorkAgentHolder - agentWorkClient := clientHolder.ManifestWorks(consumer.Name) - - // POST responses per openapi spec: 201, 400, 409, 500 - - // 201 Created - resource, resp, err := client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") - Expect(*resource.Kind).To(Equal("Resource")) - Expect(*resource.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resources/%s", *resource.Id))) - - // 400 bad request. posting junk json is one way to trigger 400. - jwtToken := ctx.Value(openapi.ContextAccessToken) - restyResp, err := resty.R(). - SetHeader("Content-Type", "application/json"). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", jwtToken)). - SetBody(`{ this is invalid }`). - Post(h.RestURL("/resources")) - - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest)) - - var work *workv1.ManifestWork - Eventually(func() error { - // ensure the work can be get by work client - work, err = agentWorkClient.Get(ctx, *resource.Id, metav1.GetOptions{}) - if err != nil { - return err - } - return nil - }, 10*time.Second, 1*time.Second).Should(Succeed()) - - Expect(work).NotTo(BeNil()) - Expect(work.Spec.Workload).NotTo(BeNil()) - Expect(len(work.Spec.Workload.Manifests)).To(Equal(1)) - manifest := map[string]interface{}{} - Expect(json.Unmarshal(work.Spec.Workload.Manifests[0].Raw, &manifest)).NotTo(HaveOccurred(), "Error unmarshalling manifest: %v", err) - Expect(manifest).To(Equal(res.Manifest)) - - statusFeedbackValue := `{"replicas":1,"availableReplicas":1,"readyReplicas":1,"updatedReplicas":1}` - newWorkStatus := workv1.ManifestWorkStatus{ - ResourceStatus: workv1.ManifestResourceStatus{ - Manifests: []workv1.ManifestCondition{ - { - Conditions: []metav1.Condition{ - { - Type: "Applied", - Status: metav1.ConditionTrue, - }, - }, - StatusFeedbacks: workv1.StatusFeedbackResult{ - Values: []workv1.FeedbackValue{ - { - Name: "status", - Value: workv1.FieldValue{ - Type: workv1.JsonRaw, - JsonRaw: &statusFeedbackValue, - }, - }, - }, - }, - }, - }, - }, - } - - // update the work status - Expect(updateWorkStatus(ctx, agentWorkClient, work, newWorkStatus)).NotTo(HaveOccurred()) - - var newRes *openapi.Resource - Eventually(func() error { - newRes, _, err = client.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resource.Id).Execute() - if err != nil { - return err - } - if newRes.Status == nil || len(newRes.Status) == 0 || - newRes.Status["ReconcileStatus"] == nil || newRes.Status["ContentStatus"] == nil { - return fmt.Errorf("resource status is empty") - } - return nil - }, 10*time.Second, 1*time.Second).Should(Succeed()) - - Expect(err).NotTo(HaveOccurred(), "Error getting resource: %v", err) - Expect(newRes.Version).To(Equal(resource.Version)) - Expect(newRes.Status["ReconcileStatus"]).NotTo(BeNil()) - reconcileStatus := newRes.Status["ReconcileStatus"].(map[string]interface{}) - observedVersion, ok := reconcileStatus["ObservedVersion"].(float64) - Expect(ok).To(BeTrue()) - Expect(int32(observedVersion)).To(Equal(*resource.Version)) - conditions := reconcileStatus["Conditions"].([]interface{}) - Expect(len(conditions)).To(Equal(1)) - condition := conditions[0].(map[string]interface{}) - Expect(condition["type"]).To(Equal("Applied")) - Expect(condition["status"]).To(Equal("True")) - - contentStatus := newRes.Status["ContentStatus"].(map[string]interface{}) - Expect(contentStatus["replicas"]).To(Equal(float64(1))) - Expect(contentStatus["availableReplicas"]).To(Equal(float64(1))) - Expect(contentStatus["readyReplicas"]).To(Equal(float64(1))) - Expect(contentStatus["updatedReplicas"]).To(Equal(float64(1))) - if h.Broker != "grpc" { - time.Sleep(1 * time.Second) - families := getServerMetrics(t, "http://localhost:8080/metrics") - labels := []*prommodel.LabelPair{ - {Name: strPtr("source"), Value: strPtr("maestro")}, - {Name: strPtr("original_source"), Value: strPtr("none")}, - {Name: strPtr("cluster"), Value: strPtr(clusterName)}, - {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, - {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceSpec))}, - {Name: strPtr("action"), Value: strPtr("create_request")}, - } - checkServerCounterMetric(t, families, "cloudevents_sent_total", labels, 1.0) - labels = []*prommodel.LabelPair{ - {Name: strPtr("source"), Value: strPtr("maestro")}, - {Name: strPtr("cluster"), Value: strPtr(clusterName)}, - {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, - {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceSpec))}, - {Name: strPtr("action"), Value: strPtr("create_request")}, - } - checkServerCounterMetric(t, families, "cloudevents_received_total", labels, 1.0) - labels = []*prommodel.LabelPair{ - {Name: strPtr("source"), Value: strPtr(clusterName)}, - {Name: strPtr("original_source"), Value: strPtr("maestro")}, - {Name: strPtr("cluster"), Value: strPtr(clusterName)}, - {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, - {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceStatus))}, - {Name: strPtr("action"), Value: strPtr("update_request")}, - } - checkServerCounterMetric(t, families, "cloudevents_sent_total", labels, 1.0) - labels = []*prommodel.LabelPair{ - {Name: strPtr("source"), Value: strPtr(clusterName)}, - {Name: strPtr("cluster"), Value: strPtr(clusterName)}, - {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, - {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceStatus))}, - {Name: strPtr("action"), Value: strPtr("update_request")}, - } - checkServerCounterMetric(t, families, "cloudevents_received_total", labels, 1.0) + families := getServerMetrics(t, "http://localhost:8080/metrics") + labels := []*prommodel.LabelPair{ + {Name: strPtr("method"), Value: strPtr("GET")}, + {Name: strPtr("path"), Value: strPtr("/api/maestro/v1/resources/-")}, + {Name: strPtr("code"), Value: strPtr("200")}, } -} - -func TestResourcePostWithoutName(t *testing.T) { - h, client := test.RegisterIntegration(t) - account := h.NewRandAccount() - ctx, cancel := context.WithCancel(h.NewAuthenticatedContext(account)) - - clusterName := "cluster-" + rand.String(5) - consumer := h.CreateConsumer(clusterName) - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.NewAPIResource(consumer.Name, deployName, 1) - h.StartControllerManager(ctx) - resourceService := h.Env().Services.Resources() - // POST responses per openapi spec: 201, 400, 409, 500 - - // 201 Created - resource, resp, err := client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") - Expect(*resource.Name).To(Equal(*resource.Id)) - - // 201 Created - resource, resp, err = client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") - Expect(*resource.Name).To(Equal(*resource.Id)) - - Eventually(func() error { - newRes, err := resourceService.List(types.ListOptions{ClusterName: clusterName, CloudEventsDataType: payload.ManifestEventDataType}) - if err != nil { - return err - } - if len(newRes) != 2 { - return fmt.Errorf("should create two resources") - } - return nil - }, 10*time.Second, 1*time.Second).Should(Succeed()) - - // make sure controller manager and work agent are stopped - cancel() -} - -func TestResourcePostWithName(t *testing.T) { - h, client := test.RegisterIntegration(t) - account := h.NewRandAccount() - ctx, cancel := context.WithCancel(h.NewAuthenticatedContext(account)) - - clusterName := "cluster-" + rand.String(5) - consumer := h.CreateConsumer(clusterName) - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.NewAPIResource(consumer.Name, deployName, 1) - h.StartControllerManager(ctx) - // POST responses per openapi spec: 201, 400, 409, 500 - - // 201 Created - resourceName := "ngix" - res.Name = &resourceName - resource, resp, err := client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*resource.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") - Expect(*resource.Name).To(Equal(resourceName)) - - // 201 Created - _, _, err = client.DefaultApi.ApiMaestroV1ResourcesPost(ctx).Resource(res).Execute() - Expect(err).To(HaveOccurred()) - - // make sure controller manager and work agent are stopped - cancel() -} - -func TestResourcePatch(t *testing.T) { - h, client := test.RegisterIntegration(t) - account := h.NewRandAccount() - ctx, cancel := context.WithCancel(h.NewAuthenticatedContext(account)) - defer func() { - cancel() - }() - // use the consumer id as the consumer name - consumer := h.CreateConsumer("") - - h.StartControllerManager(ctx) - h.StartWorkAgent(ctx, consumer.ID) - clientHolder := h.WorkAgentHolder - agentWorkClient := clientHolder.ManifestWorks(consumer.ID) - - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.CreateResource(consumer.ID, deployName, 1) - Expect(res.Version).To(Equal(int32(1))) - - var work *workv1.ManifestWork - Eventually(func() error { - // ensure the work can be get by work client - var err error - work, err = agentWorkClient.Get(ctx, res.ID, metav1.GetOptions{}) - if err != nil { - return err - } - // add finalizer to the work - patchBytes, err := json.Marshal(map[string]interface{}{ - "metadata": map[string]interface{}{ - "uid": work.GetUID(), - "resourceVersion": work.GetResourceVersion(), - "finalizers": []string{"work-test-finalizer"}, - }, - }) - if err != nil { - return err - } - - _, err = agentWorkClient.Patch(ctx, work.Name, k8stypes.MergePatchType, patchBytes, metav1.PatchOptions{}) - return err - }, 20*time.Second, 2*time.Second).Should(Succeed()) - - // 200 OK - newRes := h.NewAPIResource(consumer.Name, deployName, 2) - resource, resp, err := client.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, res.ID).ResourcePatchRequest(openapi.ResourcePatchRequest{Version: &res.Version, Manifest: newRes.Manifest}).Execute() - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*resource.Id).To(Equal(res.ID)) - Expect(*resource.CreatedAt).To(BeTemporally("~", res.CreatedAt)) - Expect(*resource.Kind).To(Equal("Resource")) - Expect(*resource.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resources/%s", *resource.Id))) - Expect(*resource.Version).To(Equal(res.Version + 1)) - Expect(resource.Manifest).To(Equal(map[string]interface{}(newRes.Manifest))) - - jwtToken := ctx.Value(openapi.ContextAccessToken) - // 500 server error. posting junk json is one way to trigger 500. - restyResp, err := resty.R(). - SetHeader("Content-Type", "application/json"). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", jwtToken)). - SetBody(`{ this is invalid }`). - Patch(h.RestURL("/resources/foo")) - - Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(restyResp.StatusCode()).To(Equal(http.StatusBadRequest)) - - dao := dao.NewEventDao(&h.Env().Database.SessionFactory) - events, err := dao.All(ctx) - Expect(err).NotTo(HaveOccurred(), "Error getting events: %v", err) - Expect(len(events)).To(Equal(2), "expected Create and Update events") - Expect(contains(api.CreateEventType, events)).To(BeTrue()) - Expect(contains(api.UpdateEventType, events)).To(BeTrue()) - - // 409 conflict error. using an out of date resource version - _, resp, err = client.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, res.ID).ResourcePatchRequest( - openapi.ResourcePatchRequest{Version: &res.Version, Manifest: newRes.Manifest}).Execute() - Expect(err).To(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusConflict)) - - Eventually(func() error { - // ensure the work can be get by work client - work, err = agentWorkClient.Get(ctx, *resource.Id, metav1.GetOptions{}) - if err != nil { - return err - } - - // ensure the work version is updated - if work.GetResourceVersion() != "2" { - return fmt.Errorf("unexpected work version %v", work.GetResourceVersion()) - } - - return nil - }, 10*time.Second, 1*time.Second).Should(Succeed()) - - Expect(work).NotTo(BeNil()) - Expect(work.Spec.Workload).NotTo(BeNil()) - Expect(len(work.Spec.Workload.Manifests)).To(Equal(1)) - manifest := map[string]interface{}{} - Expect(json.Unmarshal(work.Spec.Workload.Manifests[0].Raw, &manifest)).NotTo(HaveOccurred(), "Error unmarshalling manifest: %v", err) - Expect(manifest).To(Equal(newRes.Manifest)) - - // initialize resource deletion - _, err = client.DefaultApi.ApiMaestroV1ResourcesIdDelete(ctx, res.ID).Execute() - Expect(err).NotTo(HaveOccurred(), "Error deleting object: %v", err) - - // patch the deleting resource should return 409 conflict - _, resp, err = client.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, res.ID).ResourcePatchRequest( - openapi.ResourcePatchRequest{Version: &res.Version, Manifest: newRes.Manifest}).Execute() - Expect(err).To(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusConflict)) + checkServerCounterMetric(t, families, "rest_api_inbound_request_count", labels, 1.0) } func TestResourcePaging(t *testing.T) { @@ -411,9 +81,10 @@ func TestResourcePaging(t *testing.T) { ctx := h.NewAuthenticatedContext(account) // Paging - consumer := h.CreateConsumer("cluster-" + rand.String(5)) - _ = h.CreateResourceList(consumer.Name, 20) - _ = h.CreateResourceBundleList(consumer.Name, 20) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) + _, err = h.CreateResourceList(consumer.Name, 20) + Expect(err).NotTo(HaveOccurred()) list, _, err := client.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Execute() Expect(err).NotTo(HaveOccurred(), "Error getting resource list: %v", err) @@ -438,95 +109,30 @@ func TestResourceListSearch(t *testing.T) { account := h.NewRandAccount() ctx := h.NewAuthenticatedContext(account) - consumer := h.CreateConsumer("cluster-" + rand.String(5)) - resources := h.CreateResourceList(consumer.Name, 20) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) + resources, err := h.CreateResourceList(consumer.Name, 20) + Expect(err).NotTo(HaveOccurred()) - search := fmt.Sprintf("id in ('%s')", resources[0].ID) + search := fmt.Sprintf("name = '%s' and consumer_name = '%s'", resources[0].Name, consumer.Name) list, _, err := client.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Search(search).Execute() Expect(list.Kind).To(Equal("ResourceList")) Expect(err).NotTo(HaveOccurred(), "Error getting resource list: %v", err) Expect(len(list.Items)).To(Equal(1)) Expect(list.Total).To(Equal(int32(1))) Expect(*list.Items[0].Id).To(Equal(resources[0].ID)) -} - -func TestResourceBundleGet(t *testing.T) { - h, client := test.RegisterIntegration(t) - - account := h.NewRandAccount() - ctx := h.NewAuthenticatedContext(account) - - // 401 using no JWT token - _, _, err := client.DefaultApi.ApiMaestroV1ResourcesIdGet(context.Background(), "foo").Execute() - Expect(err).To(HaveOccurred(), "Expected 401 but got nil error") - - // GET responses per openapi spec: 200 and 404, - _, resp, err := client.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, "foo").Execute() - Expect(err).To(HaveOccurred(), "Expected 404") - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) - - consumer := h.CreateConsumer("cluster-" + rand.String(5)) - deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - resourceBundle := h.CreateResourceBundle(consumer.Name, deployName, 1) - - resBundle, resp, err := client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, resourceBundle.ID).Execute() - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - - Expect(*resBundle.Id).To(Equal(resourceBundle.ID), "found object does not match test object") - Expect(*resBundle.Name).To(Equal(resourceBundle.Name)) - Expect(*resBundle.Kind).To(Equal("ResourceBundle")) - Expect(*resBundle.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resource-bundles/%s", resourceBundle.ID))) - Expect(*resBundle.CreatedAt).To(BeTemporally("~", resourceBundle.CreatedAt)) - Expect(*resBundle.UpdatedAt).To(BeTemporally("~", resourceBundle.UpdatedAt)) - Expect(*resBundle.Version).To(Equal(resourceBundle.Version)) - - families := getServerMetrics(t, "http://localhost:8080/metrics") - labels := []*prommodel.LabelPair{ - {Name: strPtr("method"), Value: strPtr("GET")}, - {Name: strPtr("path"), Value: strPtr("/api/maestro/v1/resource-bundles/-")}, - {Name: strPtr("code"), Value: strPtr("200")}, - } - checkServerCounterMetric(t, families, "rest_api_inbound_request_count", labels, 1.0) -} - -func TestResourceBundleListSearch(t *testing.T) { - h, client := test.RegisterIntegration(t) - - account := h.NewRandAccount() - ctx := h.NewAuthenticatedContext(account) - - consumer := h.CreateConsumer("cluster-" + rand.String(5)) - resourceBundles := h.CreateResourceBundleList(consumer.Name, 20) - _ = h.CreateResourceList(consumer.Name, 20) - - search := fmt.Sprintf("name = '%s' and consumer_name = '%s'", resourceBundles[0].Name, consumer.Name) - list, _, err := client.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx).Search(search).Execute() - Expect(list.Kind).To(Equal("ResourceBundleList")) - Expect(err).NotTo(HaveOccurred(), "Error getting resource bundle list: %v", err) - Expect(len(list.Items)).To(Equal(1)) - Expect(list.Total).To(Equal(int32(1))) - Expect(*list.Items[0].Id).To(Equal(resourceBundles[0].ID)) - Expect(*list.Items[0].Name).To(Equal(resourceBundles[0].Name)) + Expect(*list.Items[0].Name).To(Equal(resources[0].Name)) search = fmt.Sprintf("consumer_name = '%s'", consumer.Name) - list, _, err = client.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx).Search(search).Execute() - Expect(list.Kind).To(Equal("ResourceBundleList")) - Expect(err).NotTo(HaveOccurred(), "Error getting resource bundle list: %v", err) + list, _, err = client.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Search(search).Execute() + Expect(list.Kind).To(Equal("ResourceList")) + Expect(err).NotTo(HaveOccurred(), "Error getting resource list: %v", err) Expect(len(list.Items)).To(Equal(20)) Expect(list.Total).To(Equal(int32(20))) - - families := getServerMetrics(t, "http://localhost:8080/metrics") - labels := []*prommodel.LabelPair{ - {Name: strPtr("method"), Value: strPtr("GET")}, - {Name: strPtr("path"), Value: strPtr("/api/maestro/v1/resource-bundles")}, - {Name: strPtr("code"), Value: strPtr("200")}, - } - checkServerCounterMetric(t, families, "rest_api_inbound_request_count", labels, 2.0) } func TestUpdateResourceWithRacingRequests(t *testing.T) { - h, client := test.RegisterIntegration(t) + h, _ := test.RegisterIntegration(t) account := h.NewRandAccount() ctx := h.NewAuthenticatedContext(account) @@ -542,10 +148,14 @@ func TestUpdateResourceWithRacingRequests(t *testing.T) { rows.Close() time.Sleep(time.Second) - consumer := h.CreateConsumer("cluster-" + rand.String(5)) + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.CreateResource(consumer.Name, deployName, 1) - newRes := h.NewAPIResource(consumer.Name, deployName, 2) + resource, err := h.CreateResource(consumer.Name, deployName, "default", 1) + Expect(err).NotTo(HaveOccurred()) + newResource, err := h.NewResource(consumer.Name, deployName, "default", 2, resource.Version) + Expect(err).NotTo(HaveOccurred()) + newResource.ID = resource.ID // starts 20 threads to update this resource at the same time threads := 20 @@ -556,9 +166,8 @@ func TestUpdateResourceWithRacingRequests(t *testing.T) { for i := 0; i < threads; i++ { go func() { defer wg.Done() - _, resp, err := client.DefaultApi.ApiMaestroV1ResourcesIdPatch(ctx, res.ID).ResourcePatchRequest( - openapi.ResourcePatchRequest{Version: &res.Version, Manifest: newRes.Manifest}).Execute() - if err != nil && resp.StatusCode == http.StatusConflict { + _, err := h.UpdateResource(newResource) + if err != nil && strings.Contains(err.Error(), fmt.Sprintf("%d", errors.ErrorConflict)) { conflictRequests = conflictRequests + 1 } }() @@ -576,7 +185,7 @@ func TestUpdateResourceWithRacingRequests(t *testing.T) { updatedCount := 0 for _, e := range events { - if e.SourceID == res.ID && e.EventType == api.UpdateEventType { + if e.SourceID == resource.ID && e.EventType == api.UpdateEventType { updatedCount = updatedCount + 1 } } @@ -605,6 +214,84 @@ func TestUpdateResourceWithRacingRequests(t *testing.T) { }, 20*time.Second, 1*time.Second).Should(Succeed()) } +func TestResourceBundleGet(t *testing.T) { + h, client := test.RegisterIntegration(t) + + account := h.NewRandAccount() + ctx := h.NewAuthenticatedContext(account) + + // 401 using no JWT token + _, _, err := client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(context.Background(), "foo").Execute() + Expect(err).To(HaveOccurred(), "Expected 401 but got nil error") + + // GET responses per openapi spec: 200 and 404, + _, resp, err := client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, "foo").Execute() + Expect(err).To(HaveOccurred(), "Expected 404") + Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) + deployName := fmt.Sprintf("nginx-%s", rand.String(5)) + resourceBundle, err := h.CreateResource(consumer.Name, deployName, "default", 1) + Expect(err).NotTo(HaveOccurred()) + + rb, resp, err := client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, resourceBundle.ID).Execute() + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + Expect(*rb.Id).To(Equal(resourceBundle.ID), "found object does not match test object") + Expect(*rb.Name).To(Equal(resourceBundle.Name)) + Expect(*rb.Kind).To(Equal("ResourceBundle")) + Expect(*rb.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resource-bundles/%s", resourceBundle.ID))) + Expect(*rb.CreatedAt).To(BeTemporally("~", resourceBundle.CreatedAt)) + Expect(*rb.UpdatedAt).To(BeTemporally("~", resourceBundle.UpdatedAt)) + Expect(*rb.Version).To(Equal(resourceBundle.Version)) + + families := getServerMetrics(t, "http://localhost:8080/metrics") + labels := []*prommodel.LabelPair{ + {Name: strPtr("method"), Value: strPtr("GET")}, + {Name: strPtr("path"), Value: strPtr("/api/maestro/v1/resource-bundles/-")}, + {Name: strPtr("code"), Value: strPtr("200")}, + } + checkServerCounterMetric(t, families, "rest_api_inbound_request_count", labels, 1.0) +} + +func TestResourceBundleListSearch(t *testing.T) { + h, client := test.RegisterIntegration(t) + + account := h.NewRandAccount() + ctx := h.NewAuthenticatedContext(account) + + consumer, err := h.CreateConsumer("cluster-" + rand.String(5)) + Expect(err).NotTo(HaveOccurred()) + resourceBundles, err := h.CreateResourceList(consumer.Name, 20) + Expect(err).NotTo(HaveOccurred()) + + search := fmt.Sprintf("name = '%s' and consumer_name = '%s'", resourceBundles[0].Name, consumer.Name) + list, _, err := client.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx).Search(search).Execute() + Expect(list.Kind).To(Equal("ResourceBundleList")) + Expect(err).NotTo(HaveOccurred(), "Error getting resource bundle list: %v", err) + Expect(len(list.Items)).To(Equal(1)) + Expect(list.Total).To(Equal(int32(1))) + Expect(*list.Items[0].Id).To(Equal(resourceBundles[0].ID)) + Expect(*list.Items[0].Name).To(Equal(resourceBundles[0].Name)) + + search = fmt.Sprintf("consumer_name = '%s'", consumer.Name) + list, _, err = client.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx).Search(search).Execute() + Expect(list.Kind).To(Equal("ResourceBundleList")) + Expect(err).NotTo(HaveOccurred(), "Error getting resource bundle list: %v", err) + Expect(len(list.Items)).To(Equal(20)) + Expect(list.Total).To(Equal(int32(20))) + + families := getServerMetrics(t, "http://localhost:8080/metrics") + labels := []*prommodel.LabelPair{ + {Name: strPtr("method"), Value: strPtr("GET")}, + {Name: strPtr("path"), Value: strPtr("/api/maestro/v1/resource-bundles")}, + {Name: strPtr("code"), Value: strPtr("200")}, + } + checkServerCounterMetric(t, families, "rest_api_inbound_request_count", labels, 2.0) +} + func TestResourceFromGRPC(t *testing.T) { h, client := test.RegisterIntegration(t) account := h.NewRandAccount() @@ -616,10 +303,12 @@ func TestResourceFromGRPC(t *testing.T) { }() // create a mock resource clusterName := "cluster-" + rand.String(5) - consumer := h.CreateConsumer(clusterName) + consumer, err := h.CreateConsumer(clusterName) + Expect(err).NotTo(HaveOccurred()) deployName := fmt.Sprintf("nginx-%s", rand.String(5)) - res := h.NewResource(consumer.Name, deployName, 1, 1) - res.ID = uuid.NewString() + resource, err := h.NewResource(consumer.Name, deployName, "default", 1, 1) + Expect(err).NotTo(HaveOccurred()) + resource.ID = uuid.NewString() h.StartControllerManager(ctx) h.StartWorkAgent(ctx, consumer.Name) @@ -628,35 +317,35 @@ func TestResourceFromGRPC(t *testing.T) { // use grpc client to create resource h.StartGRPCResourceSourceClient() - err := h.GRPCSourceClient.Publish(ctx, types.CloudEventsType{ + err = h.GRPCSourceClient.Publish(ctx, types.CloudEventsType{ CloudEventsDataType: payload.ManifestBundleEventDataType, SubResource: types.SubResourceSpec, Action: common.CreateRequestAction, - }, res) + }, resource) Expect(err).NotTo(HaveOccurred(), "Error publishing resource with grpc source client: %v", err) // for real case, the controller should have a mapping between resource (replicated) in maestro and resource (root) in kubernetes // so call subscribe method can return the resource // for testing, just list the resource via restful api. - resourceBundles, _, err := client.DefaultApi.ApiMaestroV1ResourceBundlesGet(ctx).Execute() + resources, _, err := client.DefaultApi.ApiMaestroV1ResourcesGet(ctx).Execute() Expect(err).NotTo(HaveOccurred(), "Error getting object: %v", err) - Expect(resourceBundles.Items).NotTo(BeEmpty(), "Expected returned resource list is not empty") + Expect(resources.Items).NotTo(BeEmpty(), "Expected returned resource list is not empty") - resourceBundle, resp, err := client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, *resourceBundles.Items[0].Id).Execute() + res, resp, err := client.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, *resources.Items[0].Id).Execute() Expect(err).NotTo(HaveOccurred(), "Error getting object: %v", err) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*resourceBundle.Id).To(Equal(res.ID)) - Expect(*resourceBundle.Kind).To(Equal("ResourceBundle")) - Expect(*resourceBundle.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resource-bundles/%s", *resourceBundle.Id))) - Expect(*resourceBundle.Version).To(Equal(int32(1))) + Expect(*res.Id).To(Equal(resource.ID)) + Expect(*res.Kind).To(Equal("Resource")) + Expect(*res.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resources/%s", *res.Id))) + Expect(*res.Version).To(Equal(int32(1))) // add the resource to the store - h.Store.Add(res) + h.Store.Add(resource) var work *workv1.ManifestWork Eventually(func() error { // ensure the work can be get by work client - work, err = agentWorkClient.Get(ctx, res.ID, metav1.GetOptions{}) + work, err = agentWorkClient.Get(ctx, resource.ID, metav1.GetOptions{}) if err != nil { return err } @@ -701,55 +390,58 @@ func TestResourceFromGRPC(t *testing.T) { Expect(updateWorkStatus(ctx, agentWorkClient, work, newWorkStatus)).NotTo(HaveOccurred()) Eventually(func() error { - newRes, err := h.Store.Get(res.ID) + foundResource, err := h.Store.Get(resource.ID) if err != nil { return err } - if newRes.Status == nil || len(newRes.Status) == 0 { + if foundResource.Status == nil || len(foundResource.Status) == 0 { return fmt.Errorf("resource status is empty") } - resourceStatusJSON, err := json.Marshal(newRes.Status) + evt, err := api.JSONMAPToCloudEvent(foundResource.Status) if err != nil { - return err + return fmt.Errorf("failed to convert jsonmap to cloudevent") } - resourceStatus := &api.ResourceStatus{} - if err := json.Unmarshal(resourceStatusJSON, resourceStatus); err != nil { - return err + + manifestStatus := &payload.ManifestBundleStatus{} + if err := evt.DataAs(manifestStatus); err != nil { + return fmt.Errorf("failed to unmarshal event payload: %v", err) } - if len(resourceStatus.ReconcileStatus.Conditions) == 0 { - return fmt.Errorf("resource status is empty") + resourceStatus := manifestStatus.ResourceStatus + if len(resourceStatus) != 1 { + return fmt.Errorf("unexpected length of resourceStatus") } - if !meta.IsStatusConditionTrue(resourceStatus.ReconcileStatus.Conditions, "Applied") { + if !meta.IsStatusConditionTrue(resourceStatus[0].Conditions, "Applied") { return fmt.Errorf("resource status is not applied") } return nil }, 10*time.Second, 1*time.Second).Should(Succeed()) - newRes := h.NewResource(consumer.Name, deployName, 2, 1) - newRes.ID = *resourceBundle.Id - newRes.Version = *resourceBundle.Version + newResource, err := h.NewResource(consumer.Name, deployName, "default", 2, 1) + Expect(err).NotTo(HaveOccurred()) + newResource.ID = *res.Id + newResource.Version = *res.Version err = h.GRPCSourceClient.Publish(ctx, types.CloudEventsType{ CloudEventsDataType: payload.ManifestBundleEventDataType, SubResource: types.SubResourceSpec, Action: common.UpdateRequestAction, - }, newRes) + }, newResource) Expect(err).NotTo(HaveOccurred(), "Error publishing resource with grpc source client: %v", err) - resourceBundle, resp, err = client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, newRes.ID).Execute() + res, resp, err = client.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, newResource.ID).Execute() Expect(err).NotTo(HaveOccurred(), "Error getting object: %v", err) Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(*resourceBundle.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") - Expect(*resourceBundle.Kind).To(Equal("ResourceBundle")) - Expect(*resourceBundle.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resource-bundles/%s", *resourceBundle.Id))) - Expect(*resourceBundle.Version).To(Equal(int32(2))) + Expect(*res.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") + Expect(*res.Kind).To(Equal("Resource")) + Expect(*res.Href).To(Equal(fmt.Sprintf("/api/maestro/v1/resources/%s", *res.Id))) + Expect(*res.Version).To(Equal(int32(2))) Eventually(func() error { // ensure the work can be get by work client - work, err = agentWorkClient.Get(ctx, *resourceBundle.Id, metav1.GetOptions{}) + work, err = agentWorkClient.Get(ctx, *res.Id, metav1.GetOptions{}) if err != nil { return err } @@ -771,12 +463,12 @@ func TestResourceFromGRPC(t *testing.T) { CloudEventsDataType: payload.ManifestBundleEventDataType, SubResource: types.SubResourceSpec, Action: common.DeleteRequestAction, - }, newRes) + }, newResource) Expect(err).NotTo(HaveOccurred(), "Error publishing resource with grpc source client: %v", err) Eventually(func() error { // ensure the work can be get by work client - work, err = agentWorkClient.Get(ctx, newRes.ID, metav1.GetOptions{}) + work, err = agentWorkClient.Get(ctx, newResource.ID, metav1.GetOptions{}) if err != nil { return err } @@ -800,9 +492,9 @@ func TestResourceFromGRPC(t *testing.T) { Expect(updateWorkStatus(ctx, agentWorkClient, work, deletingWorkStatus)).NotTo(HaveOccurred()) Eventually(func() error { - resourceBundle, _, err = client.DefaultApi.ApiMaestroV1ResourceBundlesIdGet(ctx, newRes.ID).Execute() - if resourceBundle != nil { - return fmt.Errorf("resource %s is not deleted", newRes.ID) + res, _, err = client.DefaultApi.ApiMaestroV1ResourcesIdGet(ctx, newResource.ID).Execute() + if res != nil { + return fmt.Errorf("resource %s is not deleted", newResource.ID) } return nil }, 10*time.Second, 1*time.Second).Should(Succeed()) @@ -842,39 +534,37 @@ func TestResourceFromGRPC(t *testing.T) { if h.Broker != "grpc" { labels = []*prommodel.LabelPair{ {Name: strPtr("source"), Value: strPtr("maestro")}, - {Name: strPtr("original_source"), Value: strPtr("none")}, {Name: strPtr("cluster"), Value: strPtr(clusterName)}, {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceSpec))}, {Name: strPtr("action"), Value: strPtr("create_request")}, } - checkServerCounterMetric(t, families, "cloudevents_sent_total", labels, 2.0) + checkServerCounterMetric(t, families, "cloudevents_received_total", labels, 1.0) labels = []*prommodel.LabelPair{ {Name: strPtr("source"), Value: strPtr("maestro")}, - {Name: strPtr("original_source"), Value: strPtr("none")}, {Name: strPtr("cluster"), Value: strPtr(clusterName)}, {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceSpec))}, {Name: strPtr("action"), Value: strPtr("update_request")}, } - checkServerCounterMetric(t, families, "cloudevents_sent_total", labels, 2.0) + checkServerCounterMetric(t, families, "cloudevents_received_total", labels, 1.0) labels = []*prommodel.LabelPair{ {Name: strPtr("source"), Value: strPtr("maestro")}, - {Name: strPtr("original_source"), Value: strPtr("none")}, {Name: strPtr("cluster"), Value: strPtr(clusterName)}, {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceSpec))}, {Name: strPtr("action"), Value: strPtr("delete_request")}, } - checkServerCounterMetric(t, families, "cloudevents_sent_total", labels, 2.0) + checkServerCounterMetric(t, families, "cloudevents_received_total", labels, 1.0) labels = []*prommodel.LabelPair{ {Name: strPtr("source"), Value: strPtr(clusterName)}, + {Name: strPtr("original_source"), Value: strPtr("maestro")}, {Name: strPtr("cluster"), Value: strPtr(clusterName)}, {Name: strPtr("type"), Value: strPtr("io.open-cluster-management.works.v1alpha1.manifestbundles")}, {Name: strPtr("subresource"), Value: strPtr(string(types.SubResourceStatus))}, {Name: strPtr("action"), Value: strPtr("update_request")}, } - checkServerCounterMetric(t, families, "cloudevents_received_total", labels, 3.0) + checkServerCounterMetric(t, families, "cloudevents_sent_total", labels, 2.0) } } @@ -906,15 +596,6 @@ func updateWorkStatus(ctx context.Context, workClient workv1client.ManifestWorkI return nil } -func contains(et api.EventType, events api.EventList) bool { - for _, e := range events { - if e.EventType == et { - return true - } - } - return false -} - func getServerMetrics(t *testing.T, url string) map[string]*prommodel.MetricFamily { // gather metrics from metrics server from url /metrics resp, err := http.Get(url) diff --git a/test/integration/status_dispatcher_test.go b/test/integration/status_dispatcher_test.go index 2c3d1236..4937b61a 100644 --- a/test/integration/status_dispatcher_test.go +++ b/test/integration/status_dispatcher_test.go @@ -29,8 +29,10 @@ func TestStatusDispatcher(t *testing.T) { // create 2 consumers consumer1 := "xyzzy" consumer2 := "thud" - _ = h.CreateConsumer(consumer1) - _ = h.CreateConsumer(consumer2) + _, err := h.CreateConsumer(consumer1) + Expect(err).NotTo(HaveOccurred()) + _, err = h.CreateConsumer(consumer2) + Expect(err).NotTo(HaveOccurred()) // should dispatch to all consumers for current instance Eventually(func() bool { @@ -40,7 +42,7 @@ func TestStatusDispatcher(t *testing.T) { // insert a new instance and healthcheck server will mark it as ready and then add it to the hash ring instanceDao := dao.NewInstanceDao(&h.Env().Database.SessionFactory) - _, err := instanceDao.Create(ctx, &api.ServerInstance{ + _, err = instanceDao.Create(ctx, &api.ServerInstance{ Meta: api.Meta{ ID: "instance1", },