-
Notifications
You must be signed in to change notification settings - Fork 264
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add service import command (#1065)
* feat: Add service import command * fix: Fix e2e test * fix: Add retry when retrieving Configuration * fix: Reflect review feedback * fix: Fix error message Co-authored-by: Roland Huß <[email protected]> * fix: Add missing mock tests * fix: Polish unit test assertions * fix: Mark import as experimental * chore: Add changelog entry * Update CHANGELOG.adoc Co-authored-by: Roland Huß <[email protected]> * fix: Remove deprecated flag Co-authored-by: Roland Huß <[email protected]>
- Loading branch information
Showing
10 changed files
with
618 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
## kn service import | ||
|
||
Import a service and its revisions (experimental) | ||
|
||
### Synopsis | ||
|
||
Import a service and its revisions (experimental) | ||
|
||
``` | ||
kn service import FILENAME | ||
``` | ||
|
||
### Examples | ||
|
||
``` | ||
# Import a service from YAML file | ||
kn service import /path/to/file.yaml | ||
# Import a service from JSON file | ||
kn service import /path/to/file.json | ||
``` | ||
|
||
### Options | ||
|
||
``` | ||
-h, --help help for import | ||
-n, --namespace string Specify the namespace to operate in. | ||
--no-wait Do not wait for 'service import' operation to be completed. | ||
--wait Wait for 'service import' operation to be completed. (default true) | ||
--wait-timeout int Seconds to wait before giving up on waiting for service to be ready. (default 600) | ||
``` | ||
|
||
### Options inherited from parent commands | ||
|
||
``` | ||
--config string kn configuration file (default: ~/.config/kn/config.yaml) | ||
--kubeconfig string kubectl configuration file (default: ~/.kube/config) | ||
--log-http log http traffic | ||
``` | ||
|
||
### SEE ALSO | ||
|
||
* [kn service](kn_service.md) - Manage Knative services | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// Copyright © 2020 The Knative Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package service | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/util/yaml" | ||
"k8s.io/client-go/util/retry" | ||
|
||
clientv1alpha1 "knative.dev/client/pkg/apis/client/v1alpha1" | ||
"knative.dev/client/pkg/kn/commands" | ||
clientservingv1 "knative.dev/client/pkg/serving/v1" | ||
"knative.dev/pkg/kmeta" | ||
servingv1 "knative.dev/serving/pkg/apis/serving/v1" | ||
) | ||
|
||
// NewServiceImportCommand returns a new command for importing a service. | ||
func NewServiceImportCommand(p *commands.KnParams) *cobra.Command { | ||
var waitFlags commands.WaitFlags | ||
|
||
command := &cobra.Command{ | ||
Use: "import FILENAME", | ||
Short: "Import a service and its revisions (experimental)", | ||
Example: ` | ||
# Import a service from YAML file | ||
kn service import /path/to/file.yaml | ||
# Import a service from JSON file | ||
kn service import /path/to/file.json`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if len(args) != 1 { | ||
return errors.New("'kn service import' requires filename of import file as single argument") | ||
} | ||
filename := args[0] | ||
|
||
namespace, err := p.GetNamespace(cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
client, err := p.NewServingClient(namespace) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return importWithOwnerRef(client, filename, cmd.OutOrStdout(), waitFlags) | ||
}, | ||
} | ||
flags := command.Flags() | ||
commands.AddNamespaceFlags(flags, false) | ||
waitFlags.AddConditionWaitFlags(command, commands.WaitDefaultTimeout, "import", "service", "ready") | ||
|
||
return command | ||
} | ||
|
||
func importWithOwnerRef(client clientservingv1.KnServingClient, filename string, out io.Writer, waitFlags commands.WaitFlags) error { | ||
var export clientv1alpha1.Export | ||
file, err := os.Open(filename) | ||
if err != nil { | ||
return err | ||
} | ||
decoder := yaml.NewYAMLOrJSONDecoder(file, 512) | ||
err = decoder.Decode(&export) | ||
if err != nil { | ||
return err | ||
} | ||
if export.Spec.Service.Name == "" { | ||
return fmt.Errorf("provided import file doesn't contain service name, please note that only kn's custom export format is supported") | ||
} | ||
|
||
serviceName := export.Spec.Service.Name | ||
|
||
// Return error if service already exists | ||
svcExists, err := serviceExists(client, serviceName) | ||
if err != nil { | ||
return err | ||
} | ||
if svcExists { | ||
return fmt.Errorf("cannot import service '%s' in namespace '%s' because the service already exists", | ||
serviceName, client.Namespace()) | ||
} | ||
|
||
err = client.CreateService(&export.Spec.Service) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Retrieve current Configuration to be use in OwnerReference | ||
currentConf, err := getConfigurationWithRetry(client, serviceName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Create revision with current Configuration's OwnerReference | ||
if len(export.Spec.Revisions) > 0 { | ||
for _, r := range export.Spec.Revisions { | ||
tmp := r.DeepCopy() | ||
// OwnerRef ensures that Revisions are recognized by controller | ||
tmp.OwnerReferences = []metav1.OwnerReference{*kmeta.NewControllerRef(currentConf)} | ||
if err = client.CreateRevision(tmp); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
err = waitIfRequested(client, serviceName, waitFlags, "Importing", "imported", out) | ||
if err != nil { | ||
return err | ||
} | ||
return err | ||
} | ||
|
||
func getConfigurationWithRetry(client clientservingv1.KnServingClient, name string) (*servingv1.Configuration, error) { | ||
var conf *servingv1.Configuration | ||
var err error | ||
err = retry.OnError(retry.DefaultBackoff, func(err error) bool { | ||
return apierrors.IsNotFound(err) | ||
}, func() error { | ||
conf, err = client.GetConfiguration(name) | ||
return err | ||
}) | ||
return conf, err | ||
} |
Oops, something went wrong.