2015-01-17 01:52:27 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-01-17 01:52:27 +00:00
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 .
* /
2018-10-05 11:06:12 +00:00
package expose
2015-01-17 01:52:27 +00:00
import (
2016-04-03 10:44:35 +00:00
"regexp"
2015-10-12 09:12:36 +00:00
"strings"
2015-01-17 01:52:27 +00:00
2015-08-11 08:38:27 +00:00
"github.com/spf13/cobra"
2018-11-09 18:49:10 +00:00
"k8s.io/klog"
2015-08-11 08:38:27 +00:00
2018-04-19 00:02:37 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2018-08-01 17:35:43 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-04-27 15:38:34 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation"
2018-08-21 10:46:39 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
2018-04-27 15:38:34 +00:00
"k8s.io/client-go/dynamic"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2018-09-09 06:59:55 +00:00
"k8s.io/kubernetes/pkg/kubectl/generate"
generateversioned "k8s.io/kubernetes/pkg/kubectl/generate/versioned"
2018-05-21 20:01:08 +00:00
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
2018-05-16 18:19:21 +00:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2018-10-10 18:29:30 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/templates"
2015-01-17 01:52:27 +00:00
)
2016-05-20 17:49:56 +00:00
var (
2017-05-25 20:37:56 +00:00
exposeResources = ` pod (po), service (svc), replicationcontroller (rc), deployment (deploy), replicaset (rs) `
2016-04-03 10:44:35 +00:00
2017-05-25 20:37:56 +00:00
exposeLong = templates . LongDesc ( `
2016-05-20 17:49:56 +00:00
Expose a resource as a new Kubernetes service .
2015-03-11 17:22:08 +00:00
2016-05-20 17:49:56 +00:00
Looks up a deployment , service , replica set , replication controller or pod by name and uses the selector
for that resource as the selector for a new service on the specified port . A deployment or replica set
will be exposed as a service only if its selector is convertible to a selector that service supports ,
i . e . when the selector contains only the matchLabels component . Note that if no port is specified via
2016-07-24 17:01:38 +00:00
-- port and the exposed resource has multiple ports , all will be re - used by the new service . Also if no
2016-05-20 17:49:56 +00:00
labels are specified , the new service will re - use the labels from the resource it exposes .
2016-04-03 10:44:35 +00:00
2016-10-07 22:24:42 +00:00
Possible resources include ( case insensitive ) :
2015-01-17 01:52:27 +00:00
2017-05-25 20:37:56 +00:00
` + exposeResources )
2016-10-07 22:24:42 +00:00
2017-05-25 20:37:56 +00:00
exposeExample = templates . Examples ( i18n . T ( `
2016-05-20 17:49:56 +00:00
# Create a service for a replicated nginx , which serves on port 80 and connects to the containers on port 8000.
kubectl expose rc nginx -- port = 80 -- target - port = 8000
2015-01-17 01:52:27 +00:00
2016-05-20 17:49:56 +00:00
# Create a service for a replication controller identified by type and name specified in "nginx-controller.yaml" , which serves on port 80 and connects to the containers on port 8000.
kubectl expose - f nginx - controller . yaml -- port = 80 -- target - port = 8000
2015-08-11 08:38:27 +00:00
2016-05-20 17:49:56 +00:00
# Create a service for a pod valid - pod , which serves on port 444 with the name "frontend"
kubectl expose pod valid - pod -- port = 444 -- name = frontend
2015-08-26 08:06:40 +00:00
2016-05-20 17:49:56 +00:00
# Create a second service based on the above service , exposing the container port 8443 as port 443 with the name "nginx-https"
kubectl expose service nginx -- port = 443 -- target - port = 8443 -- name = nginx - https
2015-03-26 02:35:26 +00:00
2016-05-20 17:49:56 +00:00
# Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named ' video - stream ' .
kubectl expose rc streamer -- port = 4100 -- protocol = udp -- name = video - stream
2016-02-09 21:18:45 +00:00
2016-05-20 17:49:56 +00:00
# Create a service for a replicated nginx using replica set , which serves on port 80 and connects to the containers on port 8000.
kubectl expose rs nginx -- port = 80 -- target - port = 8000
2016-03-18 23:07:41 +00:00
2016-05-20 17:49:56 +00:00
# Create a service for an nginx deployment , which serves on port 80 and connects to the containers on port 8000.
2017-03-15 03:49:10 +00:00
kubectl expose deployment nginx -- port = 80 -- target - port = 8000 ` ) )
2015-02-20 21:28:43 +00:00
)
2015-02-03 17:59:21 +00:00
2018-04-19 14:41:17 +00:00
type ExposeServiceOptions struct {
FilenameOptions resource . FilenameOptions
RecordFlags * genericclioptions . RecordFlags
2018-05-02 19:15:47 +00:00
PrintFlags * genericclioptions . PrintFlags
2018-04-19 00:02:37 +00:00
PrintObj printers . ResourcePrinterFunc
DryRun bool
EnforceNamespace bool
2018-09-09 06:59:55 +00:00
Generators func ( string ) map [ string ] generate . Generator
2018-05-21 18:31:33 +00:00
CanBeExposed polymorphichelpers . CanBeExposedFunc
2018-04-19 00:02:37 +00:00
MapBasedSelectorForObject func ( runtime . Object ) ( string , error )
2018-05-21 20:01:08 +00:00
PortsForObject polymorphichelpers . PortsForObjectFunc
2018-04-19 00:02:37 +00:00
ProtocolsForObject func ( runtime . Object ) ( map [ string ] string , error )
Namespace string
Mapper meta . RESTMapper
2018-05-09 16:58:12 +00:00
DynamicClient dynamic . Interface
2018-04-27 15:38:34 +00:00
Builder * resource . Builder
2018-04-19 14:41:17 +00:00
Recorder genericclioptions . Recorder
2018-04-25 12:32:08 +00:00
genericclioptions . IOStreams
2018-04-19 14:41:17 +00:00
}
2018-04-19 00:02:37 +00:00
func NewExposeServiceOptions ( ioStreams genericclioptions . IOStreams ) * ExposeServiceOptions {
2018-04-19 14:41:17 +00:00
return & ExposeServiceOptions {
RecordFlags : genericclioptions . NewRecordFlags ( ) ,
2018-05-02 19:15:47 +00:00
PrintFlags : genericclioptions . NewPrintFlags ( "exposed" ) . WithTypeSetter ( scheme . Scheme ) ,
2018-04-19 14:41:17 +00:00
2018-04-25 12:32:08 +00:00
Recorder : genericclioptions . NoopRecorder { } ,
2018-04-19 00:02:37 +00:00
IOStreams : ioStreams ,
2018-04-19 14:41:17 +00:00
}
}
2018-04-25 12:32:08 +00:00
func NewCmdExposeService ( f cmdutil . Factory , streams genericclioptions . IOStreams ) * cobra . Command {
o := NewExposeServiceOptions ( streams )
2015-08-14 18:46:43 +00:00
2017-12-15 07:20:13 +00:00
validArgs := [ ] string { }
2017-05-25 20:37:56 +00:00
resources := regexp . MustCompile ( ` \s*, ` ) . Split ( exposeResources , - 1 )
2016-04-03 10:44:35 +00:00
for _ , r := range resources {
validArgs = append ( validArgs , strings . Fields ( r ) [ 0 ] )
}
2015-02-20 21:28:43 +00:00
cmd := & cobra . Command {
2018-10-05 19:59:38 +00:00
Use : "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP|SCTP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]" ,
2017-10-11 06:26:02 +00:00
DisableFlagsInUseLine : true ,
2018-10-05 19:59:38 +00:00
Short : i18n . T ( "Take a replication controller, service, deployment or pod and expose it as a new Kubernetes Service" ) ,
Long : exposeLong ,
Example : exposeExample ,
2015-01-17 01:52:27 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-04-19 14:41:17 +00:00
cmdutil . CheckErr ( o . Complete ( f , cmd ) )
2018-04-19 00:02:37 +00:00
cmdutil . CheckErr ( o . RunExpose ( cmd , args ) )
2015-01-17 01:52:27 +00:00
} ,
2018-05-07 19:25:40 +00:00
ValidArgs : validArgs ,
2015-01-17 01:52:27 +00:00
}
2018-04-19 14:41:17 +00:00
o . RecordFlags . AddFlags ( cmd )
2018-04-19 00:02:37 +00:00
o . PrintFlags . AddFlags ( cmd )
2018-04-19 14:41:17 +00:00
2017-02-07 06:26:44 +00:00
cmd . Flags ( ) . String ( "generator" , "service/v2" , i18n . T ( "The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'." ) )
cmd . Flags ( ) . String ( "protocol" , "" , i18n . T ( "The network protocol for the service to be created. Default is 'TCP'." ) )
cmd . Flags ( ) . String ( "port" , "" , i18n . T ( "The port that the service should serve on. Copied from the resource being exposed, if unspecified" ) )
2017-06-16 07:38:17 +00:00
cmd . Flags ( ) . String ( "type" , "" , i18n . T ( "Type for this service: ClusterIP, NodePort, LoadBalancer, or ExternalName. Default is 'ClusterIP'." ) )
2017-12-12 20:16:06 +00:00
cmd . Flags ( ) . String ( "load-balancer-ip" , "" , i18n . T ( "IP to assign to the LoadBalancer. If empty, an ephemeral IP will be created and used (cloud-provider specific)." ) )
2017-03-15 03:49:10 +00:00
cmd . Flags ( ) . String ( "selector" , "" , i18n . T ( "A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set.)" ) )
2015-03-16 21:18:17 +00:00
cmd . Flags ( ) . StringP ( "labels" , "l" , "" , "Labels to apply to the service created by this call." )
2017-02-07 06:26:44 +00:00
cmd . Flags ( ) . String ( "container-port" , "" , i18n . T ( "Synonym for --target-port" ) )
2016-05-04 00:36:42 +00:00
cmd . Flags ( ) . MarkDeprecated ( "container-port" , "--container-port will be removed in the future, please use --target-port instead" )
2017-02-07 06:26:44 +00:00
cmd . Flags ( ) . String ( "target-port" , "" , i18n . T ( "Name or number for the port on the container that the service should direct traffic to. Optional." ) )
cmd . Flags ( ) . String ( "external-ip" , "" , i18n . T ( "Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP." ) )
cmd . Flags ( ) . String ( "overrides" , "" , i18n . T ( "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field." ) )
cmd . Flags ( ) . String ( "name" , "" , i18n . T ( "The name for the newly created object." ) )
cmd . Flags ( ) . String ( "session-affinity" , "" , i18n . T ( "If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'" ) )
cmd . Flags ( ) . String ( "cluster-ip" , "" , i18n . T ( "ClusterIP to be assigned to the service. Leave empty to auto-allocate, or set to 'None' to create a headless service." ) )
2015-08-11 08:38:27 +00:00
2016-08-17 18:28:07 +00:00
usage := "identifying the resource to expose a service"
2018-04-19 14:41:17 +00:00
cmdutil . AddFilenameOptionFlags ( cmd , & o . FilenameOptions , usage )
2016-05-11 00:26:39 +00:00
cmdutil . AddDryRunFlag ( cmd )
2015-11-04 21:47:08 +00:00
cmdutil . AddApplyAnnotationFlags ( cmd )
2015-01-17 01:52:27 +00:00
return cmd
}
2015-03-09 22:08:16 +00:00
2018-04-19 14:41:17 +00:00
func ( o * ExposeServiceOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command ) error {
2018-04-19 00:02:37 +00:00
o . DryRun = cmdutil . GetDryRunFlag ( cmd )
if o . DryRun {
o . PrintFlags . Complete ( "%s (dry run)" )
}
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return err
}
o . PrintObj = printer . PrintObj
2018-04-19 14:41:17 +00:00
2018-05-21 19:27:11 +00:00
o . RecordFlags . Complete ( cmd )
2018-04-19 14:41:17 +00:00
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
}
2018-04-27 15:38:34 +00:00
o . DynamicClient , err = f . DynamicClient ( )
if err != nil {
return err
}
2018-09-09 06:59:55 +00:00
o . Generators = generateversioned . GeneratorFn
2018-04-19 00:02:37 +00:00
o . Builder = f . NewBuilder ( )
2018-05-21 18:31:33 +00:00
o . CanBeExposed = polymorphichelpers . CanBeExposedFn
2018-05-23 03:28:54 +00:00
o . MapBasedSelectorForObject = polymorphichelpers . MapBasedSelectorForObjectFn
o . ProtocolsForObject = polymorphichelpers . ProtocolsForObjectFn
2018-05-21 20:01:08 +00:00
o . PortsForObject = polymorphichelpers . PortsForObjectFn
2018-05-23 03:28:54 +00:00
2018-05-16 14:54:42 +00:00
o . Mapper , err = f . ToRESTMapper ( )
2018-04-30 12:04:43 +00:00
if err != nil {
return err
}
2018-04-19 00:02:37 +00:00
2018-05-24 13:33:36 +00:00
o . Namespace , o . EnforceNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2018-04-19 00:02:37 +00:00
return err
}
func ( o * ExposeServiceOptions ) RunExpose ( cmd * cobra . Command , args [ ] string ) error {
r := o . Builder .
2018-08-02 12:36:22 +00:00
WithScheme ( scheme . Scheme , scheme . Scheme . PrioritizedVersionsAllGroups ( ) ... ) .
2015-05-13 12:14:44 +00:00
ContinueOnError ( ) .
2018-04-19 00:02:37 +00:00
NamespaceParam ( o . Namespace ) . DefaultNamespace ( ) .
FilenameParam ( o . EnforceNamespace , & o . FilenameOptions ) .
2015-05-13 12:14:44 +00:00
ResourceTypeOrNameArgs ( false , args ... ) .
Flatten ( ) .
Do ( )
2018-04-19 00:02:37 +00:00
err := r . Err ( )
2015-05-07 15:30:28 +00:00
if err != nil {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , err . Error ( ) )
2015-05-07 15:30:28 +00:00
}
2015-03-09 22:08:16 +00:00
2015-05-07 15:30:28 +00:00
// Get the generator, setup and validate all required parameters
generatorName := cmdutil . GetFlagString ( cmd , "generator" )
2018-04-19 00:02:37 +00:00
generators := o . Generators ( "expose" )
2015-11-19 18:14:10 +00:00
generator , found := generators [ generatorName ]
2015-03-09 22:08:16 +00:00
if ! found {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "generator %q not found." , generatorName )
2015-03-09 22:08:16 +00:00
}
names := generator . ParamNames ( )
2015-09-09 18:55:56 +00:00
2016-04-14 22:00:40 +00:00
err = r . Visit ( func ( info * resource . Info , err error ) error {
2015-09-09 18:55:56 +00:00
if err != nil {
2016-04-14 22:00:40 +00:00
return err
2015-04-01 21:46:33 +00:00
}
2015-09-09 18:55:56 +00:00
2016-04-14 22:00:40 +00:00
mapping := info . ResourceMapping ( )
2018-04-19 00:02:37 +00:00
if err := o . CanBeExposed ( mapping . GroupVersionKind . GroupKind ( ) ) ; err != nil {
2016-04-14 22:00:40 +00:00
return err
2015-05-04 12:17:30 +00:00
}
2016-04-14 22:00:40 +00:00
2018-09-09 06:59:55 +00:00
params := generate . MakeParams ( cmd , names )
2016-04-14 22:00:40 +00:00
name := info . Name
2016-07-24 17:01:38 +00:00
if len ( name ) > validation . DNS1035LabelMaxLength {
name = name [ : validation . DNS1035LabelMaxLength ]
2015-03-09 22:08:16 +00:00
}
2016-04-14 22:00:40 +00:00
params [ "default-name" ] = name
// For objects that need a pod selector, derive it from the exposed object in case a user
// didn't explicitly specify one via --selector
2018-09-09 06:59:55 +00:00
if s , found := params [ "selector" ] ; found && generate . IsZero ( s ) {
2018-04-19 00:02:37 +00:00
s , err := o . MapBasedSelectorForObject ( info . Object )
2016-04-14 22:00:40 +00:00
if err != nil {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "couldn't retrieve selectors via --selector flag or introspection: %v" , err )
2016-04-14 22:00:40 +00:00
}
params [ "selector" ] = s
}
2016-09-15 18:56:49 +00:00
isHeadlessService := params [ "cluster-ip" ] == "None"
2016-04-14 22:00:40 +00:00
// For objects that need a port, derive it from the exposed object in case a user
// didn't explicitly specify one via --port
2018-09-09 06:59:55 +00:00
if port , found := params [ "port" ] ; found && generate . IsZero ( port ) {
2018-04-19 00:02:37 +00:00
ports , err := o . PortsForObject ( info . Object )
2016-04-14 22:00:40 +00:00
if err != nil {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "couldn't find port via --port flag or introspection: %v" , err )
2016-04-14 22:00:40 +00:00
}
switch len ( ports ) {
case 0 :
2016-09-15 18:56:49 +00:00
if ! isHeadlessService {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "couldn't find port via --port flag or introspection" )
2016-09-15 18:56:49 +00:00
}
2016-04-14 22:00:40 +00:00
case 1 :
params [ "port" ] = ports [ 0 ]
default :
params [ "ports" ] = strings . Join ( ports , "," )
}
}
2016-05-12 04:07:07 +00:00
// Always try to derive protocols from the exposed object, may use
// different protocols for different ports.
if _ , found := params [ "protocol" ] ; found {
2018-04-19 00:02:37 +00:00
protocolsMap , err := o . ProtocolsForObject ( info . Object )
2016-05-12 04:07:07 +00:00
if err != nil {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "couldn't find protocol via introspection: %v" , err )
2016-05-12 04:07:07 +00:00
}
2018-09-09 06:59:55 +00:00
if protocols := generate . MakeProtocols ( protocolsMap ) ; ! generate . IsZero ( protocols ) {
2016-05-12 04:07:07 +00:00
params [ "protocols" ] = protocols
}
}
2018-09-09 06:59:55 +00:00
if generate . IsZero ( params [ "labels" ] ) {
2018-05-21 19:07:08 +00:00
labels , err := meta . NewAccessor ( ) . Labels ( info . Object )
2016-04-14 22:00:40 +00:00
if err != nil {
return err
}
2018-09-09 06:59:55 +00:00
params [ "labels" ] = generate . MakeLabels ( labels )
2016-04-14 22:00:40 +00:00
}
2018-09-09 06:59:55 +00:00
if err = generate . ValidateParams ( names , params ) ; err != nil {
2016-04-14 22:00:40 +00:00
return err
}
// Check for invalid flags used against the present generator.
2018-09-09 06:59:55 +00:00
if err := generate . EnsureFlagsValid ( cmd , generators , generatorName ) ; err != nil {
2016-04-14 22:00:40 +00:00
return err
}
// Generate new object
object , err := generator . Generate ( params )
2015-05-13 08:52:25 +00:00
if err != nil {
return err
}
2015-03-09 22:08:16 +00:00
2016-04-14 22:00:40 +00:00
if inline := cmdutil . GetFlagString ( cmd , "overrides" ) ; len ( inline ) > 0 {
2018-08-02 12:36:22 +00:00
object , err = cmdutil . Merge ( scheme . Codecs . LegacyCodec ( scheme . Scheme . PrioritizedVersionsAllGroups ( ) ... ) , object , inline )
2016-04-14 22:00:40 +00:00
if err != nil {
return err
}
}
2015-03-09 22:08:16 +00:00
2018-04-19 14:41:17 +00:00
if err := o . Recorder . Record ( object ) ; err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
2016-04-14 22:00:40 +00:00
}
2018-04-27 15:38:34 +00:00
2018-04-19 00:02:37 +00:00
if o . DryRun {
return o . PrintObj ( object , o . Out )
2016-04-14 22:00:40 +00:00
}
2018-09-14 21:55:00 +00:00
if err := kubectl . CreateOrUpdateAnnotation ( cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag ) , object , scheme . DefaultJSONEncoder ( ) ) ; err != nil {
2016-04-14 22:00:40 +00:00
return err
}
2015-03-09 22:08:16 +00:00
2018-04-27 15:38:34 +00:00
asUnstructured := & unstructured . Unstructured { }
2018-08-02 12:36:22 +00:00
if err := scheme . Scheme . Convert ( object , asUnstructured , nil ) ; err != nil {
2018-04-27 15:38:34 +00:00
return err
}
gvks , _ , err := unstructuredscheme . NewUnstructuredObjectTyper ( ) . ObjectKinds ( asUnstructured )
if err != nil {
return err
}
objMapping , err := o . Mapper . RESTMapping ( gvks [ 0 ] . GroupKind ( ) , gvks [ 0 ] . Version )
if err != nil {
return err
}
2016-04-14 22:00:40 +00:00
// Serialize the object with the annotation applied.
2018-08-01 17:35:43 +00:00
actualObject , err := o . DynamicClient . Resource ( objMapping . Resource ) . Namespace ( o . Namespace ) . Create ( asUnstructured , metav1 . CreateOptions { } )
2016-04-14 22:00:40 +00:00
if err != nil {
2016-01-22 18:33:23 +00:00
return err
}
2015-09-10 21:32:57 +00:00
2018-04-27 15:38:34 +00:00
return o . PrintObj ( actualObject , o . Out )
2016-04-14 22:00:40 +00:00
} )
2015-10-02 23:52:42 +00:00
if err != nil {
return err
}
2015-08-26 08:37:16 +00:00
return nil
2015-03-09 22:08:16 +00:00
}