feat: Add service import command#1065
Conversation
knative-prow-robot
left a comment
There was a problem hiding this comment.
@dsimansk: 4 warnings.
Details
In response to this:
Description
The PR is missing any kind of test coverage, TBD. I'd like to gather early feedback on the implementation approach here.
Changes
- Add
service importcommandReference
Fixes #654
/lint
/hold
/cc @rhuss @navidshaikh
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.
| return call.Result[0].(*servingv1.Configuration), mock.ErrorOrNil(call.Result[1]) | ||
| } | ||
|
|
||
| // Create a new revision |
There was a problem hiding this comment.
Golint comments: comment on exported method ServingRecorder.CreateRevision should be of the form "CreateRevision ...". More info.
| return mock.ErrorOrNil(call.Result[0]) | ||
| } | ||
|
|
||
| // Update the given revision |
There was a problem hiding this comment.
Golint comments: comment on exported method ServingRecorder.UpdateRevision should be of the form "UpdateRevision ...". More info.
| sr.r.Add("UpdateRevision", []interface{}{revision}, []interface{}{err}) | ||
| } | ||
|
|
||
| func (c *MockKnServingClient) UpdateRevision(revision *servingv1.Revision) error { |
There was a problem hiding this comment.
Golint comments: exported method MockKnServingClient.UpdateRevision should have comment or be unexported. More info.
|
looks good. Have two questions /comments
|
maximilien
left a comment
There was a problem hiding this comment.
/ok-to-test
Nice start. I wonder if `—dry-runs option would be nice whereby the import verifies but does not deploy. Maybe as a follow on. Anyhow, love seeing this whole export/import working out. An obvious e2e test will be something like:
kn service create testSvc —image ...
...
kn service export testSvc test.yml
...
kn service delete testSvc
...
kn service import testSvc test.ymlAnd end result is the same as first step :)
Per our conversation with @rhuss
That's intentional to avoid error states. During my local testing I've seen intermittent failures during wait phase. Those might have been caused by waiting issue that was recently fixed. Thanks for the suggestion, I'll double check if there's still an issue or not.
Thanks for the suggestion, |
| tmp.OwnerReferences = []metav1.OwnerReference{ | ||
| { | ||
| APIVersion: currentConf.APIVersion, | ||
| Kind: currentConf.Kind, | ||
| Name: currentConf.Name, | ||
| UID: currentConf.UID, | ||
| Controller: ptr.Bool(true), | ||
| BlockOwnerDeletion: ptr.Bool(true), |
There was a problem hiding this comment.
pls check whether you can use the kmeta package to set the owner reference. Below is from the serving repo
https://github.com/knative/serving/blob/5233f0c607c5a89d6a58d6faac9eed57b4877b79/pkg/reconciler/revision/resources/deploy.go#L253
There was a problem hiding this comment.
Nice find, thanks! It's exactly the function needed here.
|
@dsimansk please let me know when this PR is ready from your side (so no WIP anymore), I would then jump on the review, too. |
289b707 to
b013632
Compare
|
@rhuss ready now. 😺 /unhold |
|
@rhuss @navidshaikh @itsmurugappan There's one scenario I'm not really sure how to handle.
Cmd output
Cmd outputIt feels like inconsistent behaviour of |
It seems that controller was still handling the subsequent creation of |
IMO this is a bug on kn service export, whenever the mode is export , kn should give a yaml in the Export kind instead of KSVC kind. @rhuss @navidshaikh please pitch in. |
|
I've opened a new issue to track export bug. #1087 |
4b8b24f to
79bbf1e
Compare
| // Retrieve current Configuration to be use in OwnerReference | ||
| retries := 0 | ||
| var currentConf *servingv1.Configuration | ||
| for { | ||
| currentConf, err = client.GetConfiguration(serviceName) | ||
| if err != nil { | ||
| if apierrors.IsNotFound(err) && retries < 5 { | ||
| retries++ | ||
| time.Sleep(time.Second) | ||
| continue | ||
| } else { | ||
| return err | ||
| } | ||
| } | ||
| break | ||
| } |
There was a problem hiding this comment.
I've added a simple retry-loop to retrieve Configuration to verify it it's causing e2e test to fail. The retry can be moved to client like UpdateServiceWithRetry. Let me know if there are any objections to actual retry or it could be done differently.
The code should resolve the following error.
There was a problem hiding this comment.
There's also retry.OnError from client-go utils that's usable here, if there's a need for more sophisticated approach.
There was a problem hiding this comment.
I am with fine this approach;
beside I think we can separate this retry logic in a dedicated function, but then if we'd to separate it out anyway we can consume the clint-go utils you linked. Infact we should consider using these utils wherever we find a fit to encourage consumption from these standard libs we are vendoring. (all this can be done in a subsequent iteration if thats feasible).
There was a problem hiding this comment.
I like the idea of using standard libs. I've pushed an updated version in 7b8317c. We can start small here and incrementally update UpdateServiceWithRetry or any other instances.
navidshaikh
left a comment
There was a problem hiding this comment.
suggested a few nits and a wait for service related question, also please add CHANGELOG entry
| ### Options | ||
|
|
||
| ``` | ||
| --async DEPRECATED: please use --no-wait instead. Do not wait for 'service import' operation to be completed. |
There was a problem hiding this comment.
I think we can stop --async support now, it has been 3+ releases since we introduced --no-wait?
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| "knative.dev/pkg/kmeta" | ||
| servingv1 "knative.dev/serving/pkg/apis/serving/v1" | ||
|
|
||
| "github.com/spf13/cobra" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/yaml" | ||
| "knative.dev/client/pkg/kn/commands" | ||
| clientservingv1 "knative.dev/client/pkg/serving/v1" | ||
|
|
||
| clientv1alpha1 "knative.dev/client/pkg/apis/client/v1alpha1" |
There was a problem hiding this comment.
regroup wrt third party and knative.dev/client imports?
| Use: "import FILENAME", | ||
| Short: "Import a service and its revisions", | ||
| Example: ` | ||
| # Import a service in YAML format |
There was a problem hiding this comment.
| # Import a service in YAML format | |
| # Import a service from YAML format |
| # Import a service in YAML format | ||
| kn service import /path/to/file.yaml | ||
|
|
||
| # Import a service in JSON format |
There was a problem hiding this comment.
| # Import a service in JSON format | |
| # Import a service from JSON format |
| return err | ||
| } | ||
| if export.Spec.Service.Name == "" { | ||
| return fmt.Errorf("provided import doesn't content service name, please note that only kn' custom Export format is supported") |
There was a problem hiding this comment.
| return fmt.Errorf("provided import doesn't content service name, please note that only kn' custom Export format is supported") | |
| return fmt.Errorf("provided import file doesn't contain service name, please note that only kn's custom Export format is supported") |
| // Retrieve current Configuration to be use in OwnerReference | ||
| retries := 0 | ||
| var currentConf *servingv1.Configuration | ||
| for { | ||
| currentConf, err = client.GetConfiguration(serviceName) | ||
| if err != nil { | ||
| if apierrors.IsNotFound(err) && retries < 5 { | ||
| retries++ | ||
| time.Sleep(time.Second) | ||
| continue | ||
| } else { | ||
| return err | ||
| } | ||
| } | ||
| break | ||
| } |
There was a problem hiding this comment.
I am with fine this approach;
beside I think we can separate this retry logic in a dedicated function, but then if we'd to separate it out anyway we can consume the clint-go utils you linked. Infact we should consider using these utils wherever we find a fit to encourage consumption from these standard libs we are vendoring. (all this can be done in a subsequent iteration if thats feasible).
| // Create revision with current Configuration's OwnerReference | ||
| if len(export.Spec.Revisions) > 0 { | ||
| for _, r := range export.Spec.Revisions { | ||
| tmp := r |
There was a problem hiding this comment.
I think we should do deep copy here?
| } | ||
| } | ||
|
|
||
| err = waitIfRequested(client, serviceName, waitFlags, "Importing", "imported", out) |
There was a problem hiding this comment.
a service has 5 revisions among which 2 are active (configured in traffic block); will service still report true if any of those 3 non-active revisions failed to become ready ?
There was a problem hiding this comment.
The current service export impl doesn't produce such export output, only active revisions are included.
There was a problem hiding this comment.
I wonder whether we should allow later in service export, too, to export all revisions, so that the import then also would import the history. But let's leave that question for future refinement of this feature.
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
| if err != nil { | |
| return err | |
| } | |
| return nil | |
| return err |
rhuss
left a comment
There was a problem hiding this comment.
Thanks ! Looks good in general, but I'm not yet 100% sure how it behaves in the wild. E.g. whether the ordering is correct (which is also reflected in kn revision list). I'm also not sure if the export includes the latest revision generated from the service in the revision list, because in this case we do have an issue, as the initial service creation implicitly also creates a revision.
We definitely require an E2E test for the long run, to verify the full behaviour. But for now I would be happy to merge this, but also would mark the command as "experimental" (would add this to the changelog.adoc and the command description)
| serviceName, client.Namespace()) | ||
| } | ||
|
|
||
| err = client.CreateService(&export.Spec.Service) |
There was a problem hiding this comment.
when we create a service like this, then an initial revision is created. This is fine, but I think we might get into ordering issues as the generation of this revision would be 1 (and which might conflict with the revision history that we are importing, where there would be also 1 generation). At least this is what I suspect. (would need to be verified)
There was a problem hiding this comment.
Well, your suspicion is exactly on point here, revision history is mangled after import. I'll try to double-check if there's an alternative to the current import order.
➜ client git:(service-import) kn revision list
NAME SERVICE TRAFFIC TAGS GENERATION AGE CONDITIONS READY REASON
hello-xbgkn-2 hello 25% 2 60s 4 OK / 4 True
hello-brqcf-3 hello 50% 1 60s 4 OK / 4 True
hello-kphzr-1 hello 25% 1 60s 4 OK / 4 True
There was a problem hiding this comment.
Currently revision list is sorted by
// revisionListSortFunc sorts by namespace, service, generation and name
In fact it's Configuration's generation, here. That's in line with revision history in my comment above. However, I don't see any clean way how to reproduce the history in the import wihout leaning towards "replay" approach actually.
| } | ||
| } | ||
|
|
||
| err = waitIfRequested(client, serviceName, waitFlags, "Importing", "imported", out) |
There was a problem hiding this comment.
I wonder whether we should allow later in service export, too, to export all revisions, so that the import then also would import the history. But let's leave that question for future refinement of this feature.
| r.GetService("foo", nil, nil) | ||
| _, err = executeServiceCommand(client, "import", file) | ||
|
|
||
| assert.ErrorContains(t, err, "because the service already exists") |
There was a problem hiding this comment.
In general, for tests, I would assert that all required context information is included, i.e. here the service name and maybe the namespace (if this is part of the error message). The exact wording is secondary.
There was a problem hiding this comment.
This is true for the other checks below, too, that check for some messages
| return call.Result[0].(*servingv1.Configuration), mock.ErrorOrNil(call.Result[1]) | ||
| } | ||
|
|
||
| // CreateRevision records a call CreateRevision |
There was a problem hiding this comment.
Could you please also add a call to the recorder and the mock methods to client_mock_test ? This keeps the test coverage happy :)
There was a problem hiding this comment.
Sure, I've missed that.
rhuss
left a comment
There was a problem hiding this comment.
Beside the issue of building up revision with the proper generation ordering, this PR looks good to me.
I suggest, to merge this PR as now (it's still marked as experimental), but think about another solution after this.
E.g would it be possible to remove the revision that has been created implicitly when we created the Service and after we have added the revision list ? Also we could experiment with building up a Configuration first and only add the Service as a final step (with the same name as the Configuration, as this is the correlation between Service and Configuration). Also, the ownerReference needs between Service and Configuration then would be needed to be set as the last step.
wdyt, should we move forward now ?
Co-authored-by: Roland Huß <rhuss@redhat.com>
Co-authored-by: Roland Huß <rhuss@redhat.com>
b175789 to
e29755d
Compare
|
The following is the coverage report on the affected files.
|
|
@rhuss sure, sounds good to me. I've been experimenting with temp initial revision to recreate generation history correctly without too much luck yet. I can explore the options further, but it shouldn't prevent "experimental" import to be merged now. |
|
Ok, then let's go for it /Lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: dsimansk, maximilien, rhuss The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Description
The PR is missing any kind of test coverage, TBD. I'd like to gather early feedback on the implementation approach here.
Changes
service importcommandReference
Fixes #654
/lint
/hold
/cc @rhuss @navidshaikh