kubernetes-client: Unable to update status on Custom Resources

Hello, I was recently work on a CRD, but I was not able to set the Status on the CR. I’ve tried a couple of things and this is what I find:

  • I’m able to create and patch the Spec
  • I’m able to get Status (via list) set by my controller written in go
  • I’m not able to create a Resource with Status or patch the Resource with Status via my Java client.

I’m clueless right now, and any help would be awesome, thanks!

Anacron.java

public class Anacron extends CustomResource {
    private AnacronSpec spec;
    private AnacronStatus status;
    public AnacronSpec getSpec() { return spec; }
    public void setSpec(final AnacronSpec spec) { this.spec = spec; }
    public AnacronStatus getStatus() { return status; }
    public void setStatus(final AnacronStatus status) { this.status = status; }
}

AnacronList.java

public class AnacronList extends CustomResourceList<Anacron> {
}

AnacronSpec.java

@JsonPropertyOrder({"schedule", "jobTemplate", "concurrencyPolicy", "jobTemplate"})
@JsonDeserialize(
        using = JsonDeserializer.None.class
)
public class AnacronSpec implements KubernetesResource {
    @JsonProperty(value = "schedule")
    private String schedule;
    @JsonProperty(value = "concurrencyPolicy")
    private String concurrencyPolicy;
    @JsonProperty(value = "startingDeadlineSeconds")
    private int startingDeadlineSeconds;
    @JsonProperty(value = "jobTemplate")
    private JobTemplateSpec jobTemplate;

    public AnacronSpec() {
    }

    public String getConcurrencyPolicy() { return concurrencyPolicy; }
    public void setConcurrencyPolicy(final String concurrencyPolicy) { this.concurrencyPolicy = concurrencyPolicy; }
    public int getStartingDeadlineSeconds() { return startingDeadlineSeconds; }
    public void setStartingDeadlineSeconds(final int startingDeadlineSeconds) { this.startingDeadlineSeconds = startingDeadlineSeconds; }
}

AnacronStatus.java

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"apiVersion", "kind", "metadata", "active", "lastScheduleTime"})
@JsonDeserialize(
        using = JsonDeserializer.None.class
)
public class AnacronStatus implements KubernetesResource {
    @JsonProperty(value = "active")
    private List<ObjectReference> active = new ArrayList();
    @JsonProperty(value = "lastScheduleTime")
    private String lastScheduleTime;
    public AnacronStatus() {
    }

    public List<ObjectReference> getActive() { return active; }
    public void setActive(final List<ObjectReference> active) { this.active = active; }
    public String getLastScheduleTime() { return lastScheduleTime; }
    public void setLastScheduleTime(final String lastScheduleTime) { this.lastScheduleTime = lastScheduleTime; }
}

DoneableAnacron.java

public class DoneableAnacron extends CustomResourceDoneable<Anacron> {
    public DoneableAnacron(final Anacron resource, final Function function) {
        super(resource, function);
    }
}

Client Initialization

private static final CustomResourceDefinition ANACRON_CRD = new CustomResourceDefinitionBuilder()
            .withApiVersion("apiextensions.k8s.io/v1beta1")
            .withNewMetadata().withName(CRD_NAME).endMetadata()
            .withNewSpec().withGroup(CRD_GROUP).withVersion(CRD_VERSION).withScope(CRD_SCOPE)
            .withNewNames().withKind(CRD_KIND).withSingular(CRD_SINGULAR).withPlural(CRD_PLURAL).withShortNames(CRD_SHORT_NAMES).endNames().endSpec()
            .build();

final MixedOperation<Anacron, AnacronList, DoneableAnacron, Resource<Anacron, DoneableAnacron>> anacronClient = kubernetesClient.customResources(ANACRON_CRD, Anacron.class, AnacronList.class, DoneableAnacron.class);

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 3
  • Comments: 24 (12 by maintainers)

Commits related to this issue

Most upvoted comments

@adam-sandor thanks for your input. I tried that and doesn’t work either. I then did a round of tests and I think the problem lies in my CRD yaml file.

The only difference appear to be the working one doesn’t have:

  subresources:
    status: {}

Once I have this set, my status patching no longer works.

According to https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#status-subresource

This seems to be an expected behavior, because PUT/POST/PATCH requests to the custom resource ignore changes to the status stanza. What I want is PUT requests to the /status subresource take a custom resource object and ignore changes to anything except the status stanza., but I can’t find this UpdateStatus function in fabric8io/kubernetes-client, am I wrong? @rohanKanojia I don’t see the new api has this functionally either, and I’m confused by what does edit do.

If I can add to this. According to https://blog.openshift.com/kubernetes-custom-resources-grow-up-in-v1-10/, one of the key benefits of defining the status property as the standard ‘/status’ subresource is that updates to the status won’t increment the metadata.generation property. This property will only get updated when the spec gets updated. This is extremely useful for cases where the custom controller needs to update the status without triggering an unintended sync.

@adam-sandor I would therefor say that perhaps the /status subresource, as well as the /scale subresource is something special in K8S, and from the above document it would seem that they even have their own resource URL’s where they can be updated, e.g. “/apis/example.com/v1/namespaces/default/databases/mysql/status”. From what I can see it would seem that the Fabric8 Java client doesn’t support PUTs to the url of the subresource.

Hmm, I see. Let me try to prioritize this.

Ah that is very interesting @ampie ! @csviri check this out ☝️

i am currently using a quick and dirty workaround for this:

            final String statusUri = URLUtils.join(client.getMasterUrl().toString(), "apis", "myclass.com", "v1", "namespaces",
                    namespace, "my-objects", name, "status");
            infoClass.setStatus(status);
            final RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), mapper.writeValueAsBytes(infoClass));
            baseClient.getHttpClient().newCall(new Request.Builder()
                    .method("PUT", requestBody)
                    .url(statusUri)
                    .build())
                    .execute()
                    .close();

and then ignoring watcher MODIFY operations when the generation is unchanged. Still think this should be properly implemented…

We didn’t have problems with setting status on our CR using the typed API. Status isn’t a special thing in the Kubernetes API so the same rules should apply to it than any other field. I do see that our classes look quite different from your CR mapping classes:

  • We don’t specify the JSON mapping, just leave it to defaults.
  • Our class mapping Status doesn’t implement KubernetesResource. I’m guessing the second point could be the cause of the issue. Status is not something that has it’s own metadata.
public class OpsRepo extends CustomResource {
    private OpsRepoSpec spec;
    private OpsRepoStatus status;

    public OpsRepoSpec getSpec() {
        return spec;
    }

    public void setSpec(OpsRepoSpec spec) {
        this.spec = spec;
    }

    public OpsRepoStatus getStatus() {
        return status;
    }

    public void setStatus(OpsRepoStatus status) {
        this.status = status;
    }
}
public class OpsRepoStatus {

    private OpsRepoState state;
    private String repoUrl;

    public OpsRepoState getState() {
        return state;
    }

    public void setState(OpsRepoState state) {
        this.state = state;
    }

    public String getRepoUrl() {
        return repoUrl;
    }

    public void setRepoUrl(String repoUrl) {
        this.repoUrl = repoUrl;
    }
}