I'm using fabric8 and java operator sdk.
I'd like to remove one custom finalizer in the deleteResource handler of the ResourceController.
What is the suggested way to remove the finalizer and save the custom resource?
I think you should be able to add finalizers using Fabric8 Kubernetes Client. For example, if you want to add/remove finalizers like these in your Custom Resource's metadata:
finalizers:
- finalizer.stable.example.com
You can use either our typed API or typeless API for achieving this. I'm adding examples of both the approaches below using a Dummy Custom Resource provided in our CRDExample:
Typed API (Requires POJOs of Custom Resources)
MixedOperation<Dummy, DummyList, DoneableDummy, Resource<Dummy, DoneableDummy>> dummyCRClient = client
.customResources(dummyCRD, Dummy.class, DummyList.class, DoneableDummy.class);
// Fetch resource fom Kubernetes API server
Dummy dummy2 = dummyCRClient.inNamespace("default").withName("second-dummy").get();
// Get metadata
ObjectMeta objectMeta = dummy2.getMetadata();
// Modify metadata
objectMeta.setFinalizers(Collections.singletonList("finalizer.stable.example.com"));
// Patch it back in Custom Resource
dummy2.setMetadata(objectMeta);
// Patch to Kubernetes
dummyCRClient.inNamespace("default").withName("second-dummy").patch(dummy2);
Typeless API (Provides way to access raw JSON Custom Resources)
try (KubernetesClient client = new DefaultKubernetesClient()) {
CustomResourceDefinitionContext crdContext = new CustomResourceDefinitionContext.Builder()
.withGroup("demo.fabric8.io")
.withPlural("dummies")
.withScope("Namespaced")
.withVersion("v1")
.withName("dummies.demo.fabric8.io")
.build();
// Fetch resource fom Kubernetes API server
Map<String, Object> dummy2 = client.customResource(crdContext).get("default", "second-dummy");
JSONObject dummy2JsonObj = new JSONObject(dummy2);
// Get metadata
JSONObject dummy2ObjectMeta = dummy2JsonObj.getJSONObject("metadata");
// Modify metadata
dummy2ObjectMeta.put("finalizers", new String[] { "finalizer.stable.example.com"});
// Patch it back in Custom Resource
dummy2JsonObj.put("metadata", dummy2ObjectMeta);
// Patch to Kubernetes
client.customResource(crdContext).edit("default", "second-dummy", dummy2JsonObj.toString());
}
Related
I am struggling to set-up infrastructure in my solution to send and retrieve the custom header for REST WCF Service. Basically, we need this to send UserID, password, token value from client to service and if provided values are valid then operation will continue to execute otherwise throw exception.
We already have few classes inherited from interfaces like IDispatchMessageInspector, IClientMessageInspector, IEndPointBehaviour, MessageHeader, etc., This is working fine for WCF with soap request. I tried to use these classes for my new REST WCF Service, but was not working as MessageHeader derived class supports only Soap.
I also tried using WebOperationContext, but no luck :(
Please provide a solution along with sample project to solve this problem.
Thank you so much!
Seems in your case it might be easier to interogate the ASPNET pipeline
if you add the following to your WCF service to allow it to hookup into the ASPNET pipeline
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
Then you can simply now use the HttpContext object and just get the headers as you would from a normal aspnet application, e.g
System.Web.HttpContext.Current.Request.Headers["CustomHeader"]
If you want to add http header in wcf rest service , you should use HttpRequestMessageProperty, it has a Headers property , you could set http Header through its Headers property
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
HttpRequestMessageProperty property;
// if OutgoingMessageProperties already has HttpRequestMessageProperty, use the existing one , or initialize a new one and
// set OutgoingMessageProperties's HttpRequestMessageProperty.Name key's value to the initialized HttpRequestMessageProperty so that the HttpRequestMessageProperty will work
if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name)){
property = OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
}
else
{
property = new HttpRequestMessageProperty();
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = property;
}
// add headers to HttpRequestMessageProperty, it will become the http header of the reuqest
property.Headers.Add(System.Net.HttpRequestHeader.Authorization, "myAuthorization");
string re = client.HelloWorld();
}
About getting the Header , just use WebOperationContext.Current.Headers.
WebOperationContext.Current.IncomingRequest.Headers["MyCustomHttpHeader"]
Please refer to http://kenneththorman.blogspot.com/2011/02/wcf-rest-client-using-custom-http.html
I'm trying to set up a system with multiple applications connecting by use of a discovery server. I can traverse the hal responses to a specific resource, but I'm looking for a solution to get from a collection resource to a single resource and find the data for a specific entity.
In 1 application I have a RepositoryRestResource exposing some object:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {}
In some other application, I would like to get to a single thing. I have the id (let's say it's 1) and have the relation name of the collection and the single resource.
I would like to use a DiscoveredResource to get a link to this single item resource, or to the collection resource which I can then somehow expand using the ID (which would require a templated resource).
If at all possible I would not like to just add "/1" at the end of the URL.
this is how I currently create a DiscoveredResource to point to the collection resource:
new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things"));
Should I and is it possible to add a templated link on a collection resource created by a #RepositoryRestResource. Or is there some other trick I am missing?
The solution here is to add a custom method as a #RestResource which exposes a relation with a templates URL you can then follow to.
Repo:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {
#RestResource(rel = "thing")
Thing findOneById(#Param("id") Long id);
}
Discovery + traverson:
DiscoveredResource resource = new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things","search","thing"));
Link link = resource.getLink().expand(id);
Is there anyway that I can generate some metadata to add to the service when it registers.
We are moving from Eureka to Consul and I need to add a UUID value to the registered metadata when a service starts. So that later I can get this metadata value when I retrieve the service instances by name.
Some background: We were using this excellent front end UI from https://github.com/VanRoy/spring-cloud-dashboard. It is set to use the Eureka model for services in which you have an Application with a name. Each application will have multiple instances each with an instance id.
So with the eureka model there is a 2 level service description whereas the spring cloud model is a flat one where n instances each of which have a service id.
The flat model won't work with the UI that I referenced above since there is no distinction between application name and instance id which is the spring model these are the same.
So if I generate my own instance id and handle it through metadata then I can preserve some of the behaviour without rewriting the ui.
See the documentation on metadata and tags in spring cloud consul. Consul doesn't support metadata on service discovery yet, but spring cloud has a metadata abstraction (just a map of strings). In consul tags created with key=value style are parsed into that metadata map.
For example in, application.yml:
spring:
cloud:
consul:
discovery:
tags: foo=bar, baz
The above configuration will result in a map with foo→bar and baz→baz.
Based on Spencer's answer I added an EnvironmentPostProcessor to my code.
It works and I am able to add the metadata tag I want programmatically but it is a complement to the "tags: foo=bar, baz" element so it overrides that one. I will probably figure a way around it in the next day or so but I thougth I would add what I did for other who look at this answer and say, so what did you do?
first add a class as follows:
#Slf4j
public class MetaDataEnvProcessor implements EnvironmentPostProcessor, Ordered {
// Before ConfigFileApplicationListener
private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1;
private UUID instanceId = UUID.randomUUID();
#Override
public int getOrder() {
return this.order;
}
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.consul.discovery.tags", "instanceId="+instanceId.toString());
MapPropertySource propertySource = new MapPropertySource("springCloudConsulTags", map);
environment.getPropertySources().addLast(propertySource);
}
}
then add a spring.factories in resources/META-INF with eht following line to add this processor
org.springframework.boot.env.EnvironmentPostProcessor=com.example.consul.MetaDataEnvProcessor
This works fine except for the override of what is in your application.yml file for tags
I have a Sling Resource object. What is the best way to set or update its property?
It depends on the Sling version:
Sling >= 2.3.0 (since CQ 5.6)
Adapt your resource to ModifiableValueMap, use its put method and commit the resource resolver:
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
map.put("property", "value");
resource.getResourceResolver().commit();
Sling < 2.3.0 (CQ 5.5 and earlier)
Adapt your resource to PersistableValueMap, use its put and save methods:
PersistableValueMap map = resource.adaptTo(PersistableValueMap.class);
map.put("property", "value");
map.save();
JCR API
You may also adapt the resource to Node and use the JCR API to change property. However, it's a good idea to stick to one abstraction layer and in this case we somehow break the Resource abstraction provided by Sling.
Node node = resource.adaptTo(Node.class);
node.setProperty("property", "value");
node.getSession().save();
As many developers are not fond of using Node API. You can also use ValueMap and ModifiableValueMap APIs to read and update property respectively.
Read Value through ValueMap
ValueMap valueMap = resource.getValueMap();
valueMap.get("yourProperty", String.class);
Write/Modify property through ModifiableValueMap
ModifiableValueMap modifiableValueMap = resource.adaptTo(ModifiableValueMap.class);
modifiableValueMap.put("NewProperty", "Your Value"); //write
modifiableValueMap.put("OldProperty", "Updated Value"); // Modify
After writing property to a node, save these values by committing the 'resourceResolver'
You'll need system/service user for admin resourceResolver.
Go through this documentation for more info about service user.
It is not working in publish. But if user logged-In as admin it will work.
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
map.put("property", "value");
resource.getResourceResolver().commit();
ISSUE
We just switched from MVC4 Web API Beta to the RC and we're running into a Multiple actions were found that match the request ... exception in our service.
BACKGROUND
We have two POST actions defined in our ApiController:
public class MyModelController : ApiController
{
...
// POST /mymodel
public MyModel Post(MyModel model)
{
...
}
// POST /mymodel/upload
[ActionName("Upload")]
public HttpResponseMessage UploadModelImage()
{
HttpRequestMessage request = Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.UnsupportedMediaType, request));
}
...
}
}
The first action (default POST action) is used to create a new MyModel object from the JSON passed to the service. The user of our portal has the option to upload an image as part of creating a new MyModel object in which case we use the second Upload action to save the file and persist the new object to the database. This action reads the multipart request content, parses out the properties for the model and saves the image uploaded to our CDN.
Since our switch to the RC, the upload action (http://www.myapidomain.com/mymodel/upload) goes through fine, but the regular POST action (http://www.myapidomain.com/mymodel/) fails with the Multiple actions were found that match the request ... exception citing both the methods listed above as the conflicts.
Here are our routes:
routes.MapHttpRoute(
"Default", // route name
"{controller}" // route template
);
routes.MapHttpRoute(
"OnlyId", // route name
"{controller}/{id}", // route template
new {}, // defaults
new {controller = #"[^0-9]+", id = #"[0-9]+"} // constraints
);
routes.MapHttpRoute(
"OnlyAction", // route name
"{controller}/{action}", // route template
new {}, // defaults
new {controller = #"[^0-9]+", action = ActionNameConstraint.Instance} // constraints
);
routes.MapHttpRoute(
"DependantAction", // route name
"{controller}/{principalId}/{action}/{dependentId}", // route template
new {dependentId = System.Web.Http.RouteParameter.Optional}, // defaults
new {controller = #"[^0-9]+", action = ActionNameConstraint.Instance} // constraints
);
ActionNameConstraint is just a custom constraint that ensures that the {action} must belong to the {controller}
QUESTION
I've tried messing with the routes in different orders to see if that would fix the issue with no luck. I'm looking for help with any of the following solutions:
A potential issue in our routes.
An alternative solution for routing by content-type. The Upload action only needs to be called for mult-part form posts. If the content type is JSON or XML, the regular action should be used. I haven't been able to find any resources that suggest this can be done, but I'm hoping someone else has considered this.
A model-binding approach for reading file streams from the request content so we don't need the separate Upload action anymore
By default it is not possible to mix REST style routing and RPC style routing in a single controller - which it seems you are trying to do.
There is an open issue for that on ASP.NET Web Stack's codeplex, where Web API source lives - http://aspnetwebstack.codeplex.com/workitem/184.
If you want to use it like that, you need to move the upload action to a separate controller, which will be called in an RPC-only way.