diff --git a/changelogs/unreleased/1116-skriss b/changelogs/unreleased/1116-skriss new file mode 100644 index 000000000..bc54f773f --- /dev/null +++ b/changelogs/unreleased/1116-skriss @@ -0,0 +1 @@ +add ServerStatusRequest CRD and show server version in `ark version` output diff --git a/examples/common/00-prereqs.yaml b/examples/common/00-prereqs.yaml index fa9de43fe..2ec75f7ed 100644 --- a/examples/common/00-prereqs.yaml +++ b/examples/common/00-prereqs.yaml @@ -163,6 +163,20 @@ spec: kind: VolumeSnapshotLocation --- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: serverstatusrequests.ark.heptio.com + labels: + component: ark +spec: + group: ark.heptio.com + version: v1 + scope: Namespaced + names: + plural: serverstatusrequests + kind: ServerStatusRequest +--- apiVersion: v1 kind: Namespace metadata: diff --git a/pkg/apis/ark/v1/register.go b/pkg/apis/ark/v1/register.go index 9629cdd7d..ca0e501e7 100644 --- a/pkg/apis/ark/v1/register.go +++ b/pkg/apis/ark/v1/register.go @@ -69,6 +69,7 @@ func CustomResources() map[string]typeInfo { "ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}), "BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}), "VolumeSnapshotLocation": newTypeInfo("volumesnapshotlocations", &VolumeSnapshotLocation{}, &VolumeSnapshotLocationList{}), + "ServerStatusRequest": newTypeInfo("serverstatusrequests", &ServerStatusRequest{}, &ServerStatusRequestList{}), } } diff --git a/pkg/apis/ark/v1/server_status_request.go b/pkg/apis/ark/v1/server_status_request.go new file mode 100644 index 000000000..82b2d146c --- /dev/null +++ b/pkg/apis/ark/v1/server_status_request.go @@ -0,0 +1,68 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 v1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServerStatusRequest is a request to access current status information about +// the Ark server. +type ServerStatusRequest struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + + Spec ServerStatusRequestSpec `json:"spec"` + Status ServerStatusRequestStatus `json:"status,omitempty"` +} + +// ServerStatusRequestSpec is the specification for a ServerStatusRequest. +type ServerStatusRequestSpec struct { +} + +// ServerStatusRequestPhase represents the lifecycle phase of a ServerStatusRequest. +type ServerStatusRequestPhase string + +const ( + // ServerStatusRequestPhaseNew means the ServerStatusRequest has not been processed yet. + ServerStatusRequestPhaseNew ServerStatusRequestPhase = "New" + // ServerStatusRequestPhaseProcessed means the ServerStatusRequest has been processed. + ServerStatusRequestPhaseProcessed ServerStatusRequestPhase = "Processed" +) + +// ServerStatusRequestStatus is the current status of a ServerStatusRequest. +type ServerStatusRequestStatus struct { + // Phase is the current lifecycle phase of the ServerStatusRequest. + Phase ServerStatusRequestPhase `json:"phase"` + + // ProcessedTimestamp is when the ServerStatusRequest was processed + // by the ServerStatusRequestController. + ProcessedTimestamp metav1.Time `json:"processedTimestamp"` + + // ServerVersion is the Ark server version. + ServerVersion string `json:"serverVersion"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServerStatusRequestList is a list of ServerStatusRequests. +type ServerStatusRequestList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []ServerStatusRequest `json:"items"` +} diff --git a/pkg/apis/ark/v1/zz_generated.deepcopy.go b/pkg/apis/ark/v1/zz_generated.deepcopy.go index 41cef024a..986639209 100644 --- a/pkg/apis/ark/v1/zz_generated.deepcopy.go +++ b/pkg/apis/ark/v1/zz_generated.deepcopy.go @@ -1235,6 +1235,100 @@ func (in *ScheduleStatus) DeepCopy() *ScheduleStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerStatusRequest) DeepCopyInto(out *ServerStatusRequest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequest. +func (in *ServerStatusRequest) DeepCopy() *ServerStatusRequest { + if in == nil { + return nil + } + out := new(ServerStatusRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServerStatusRequest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerStatusRequestList) DeepCopyInto(out *ServerStatusRequestList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServerStatusRequest, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequestList. +func (in *ServerStatusRequestList) DeepCopy() *ServerStatusRequestList { + if in == nil { + return nil + } + out := new(ServerStatusRequestList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServerStatusRequestList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerStatusRequestSpec) DeepCopyInto(out *ServerStatusRequestSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequestSpec. +func (in *ServerStatusRequestSpec) DeepCopy() *ServerStatusRequestSpec { + if in == nil { + return nil + } + out := new(ServerStatusRequestSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServerStatusRequestStatus) DeepCopyInto(out *ServerStatusRequestStatus) { + *out = *in + in.ProcessedTimestamp.DeepCopyInto(&out.ProcessedTimestamp) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServerStatusRequestStatus. +func (in *ServerStatusRequestStatus) DeepCopy() *ServerStatusRequestStatus { + if in == nil { + return nil + } + out := new(ServerStatusRequestStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StorageType) DeepCopyInto(out *StorageType) { *out = *in diff --git a/pkg/cmd/ark/ark.go b/pkg/cmd/ark/ark.go index c84526893..d0b22462c 100644 --- a/pkg/cmd/ark/ark.go +++ b/pkg/cmd/ark/ark.go @@ -62,7 +62,7 @@ operations can also be performed as 'ark backup get' and 'ark schedule create'.` schedule.NewCommand(f), restore.NewCommand(f), server.NewCommand(), - version.NewCommand(), + version.NewCommand(f), get.NewCommand(f), describe.NewCommand(f), create.NewCommand(f), diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index d8ac6c313..7dd10e181 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -681,6 +681,17 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string wg.Done() }() + serverStatusRequestController := controller.NewServerStatusRequestController( + s.logger, + s.arkClient.ArkV1(), + s.sharedInformerFactory.Ark().V1().ServerStatusRequests(), + ) + wg.Add(1) + go func() { + serverStatusRequestController.Run(ctx, 1) + wg.Done() + }() + // SHARED INFORMERS HAVE TO BE STARTED AFTER ALL CONTROLLERS go s.sharedInformerFactory.Start(ctx.Done()) diff --git a/pkg/cmd/version/version.go b/pkg/cmd/version/version.go index 87bd73bbe..c829035c2 100644 --- a/pkg/cmd/version/version.go +++ b/pkg/cmd/version/version.go @@ -18,21 +18,133 @@ package version import ( "fmt" + "io" + "os" + "time" + "github.com/pkg/errors" "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + arkv1api "github.com/heptio/ark/pkg/apis/ark/v1" "github.com/heptio/ark/pkg/buildinfo" + "github.com/heptio/ark/pkg/client" + "github.com/heptio/ark/pkg/cmd" + arkv1client "github.com/heptio/ark/pkg/generated/clientset/versioned/typed/ark/v1" + "github.com/heptio/ark/pkg/serverstatusrequest" ) -func NewCommand() *cobra.Command { +func NewCommand(f client.Factory) *cobra.Command { + clientOnly := false + serverStatusGetter := &defaultServerStatusGetter{ + namespace: f.Namespace(), + timeout: 5 * time.Second, + } + c := &cobra.Command{ Use: "version", Short: "Print the ark version and associated image", - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("Version: %s\n", buildinfo.Version) - fmt.Printf("Git commit: %s\n", buildinfo.FormattedGitSHA()) + Run: func(c *cobra.Command, args []string) { + var arkClient arkv1client.ServerStatusRequestsGetter + + if !clientOnly { + client, err := f.Client() + cmd.CheckError(err) + + arkClient = client.ArkV1() + } + + printVersion(os.Stdout, clientOnly, arkClient, serverStatusGetter) }, } + c.Flags().DurationVar(&serverStatusGetter.timeout, "timeout", serverStatusGetter.timeout, "maximum time to wait for server version to be reported") + c.Flags().BoolVar(&clientOnly, "client-only", clientOnly, "only get ark client version, not server version") + return c } + +func printVersion(w io.Writer, clientOnly bool, client arkv1client.ServerStatusRequestsGetter, serverStatusGetter serverStatusGetter) { + fmt.Fprintln(w, "Client:") + fmt.Fprintf(w, "\tVersion: %s\n", buildinfo.Version) + fmt.Fprintf(w, "\tGit commit: %s\n", buildinfo.FormattedGitSHA()) + + if clientOnly { + return + } + + serverStatus, err := serverStatusGetter.getServerStatus(client) + if err != nil { + fmt.Fprintf(w, "\n", err) + return + } + + fmt.Fprintln(w, "Server:") + fmt.Fprintf(w, "\tVersion: %s\n", serverStatus.Status.ServerVersion) +} + +type serverStatusGetter interface { + getServerStatus(client arkv1client.ServerStatusRequestsGetter) (*arkv1api.ServerStatusRequest, error) +} + +type defaultServerStatusGetter struct { + namespace string + timeout time.Duration +} + +func (g *defaultServerStatusGetter) getServerStatus(client arkv1client.ServerStatusRequestsGetter) (*arkv1api.ServerStatusRequest, error) { + req := serverstatusrequest.NewBuilder().Namespace(g.namespace).GenerateName("ark-cli-").Build() + + created, err := client.ServerStatusRequests(g.namespace).Create(req) + if err != nil { + return nil, errors.WithStack(err) + } + defer client.ServerStatusRequests(g.namespace).Delete(created.Name, nil) + + listOptions := metav1.ListOptions{ + // TODO: once the minimum supported Kubernetes version is v1.9.0, uncomment the following line. + // See http://issue.k8s.io/51046 for details. + //FieldSelector: "metadata.name=" + req.Name + ResourceVersion: created.ResourceVersion, + } + watcher, err := client.ServerStatusRequests(g.namespace).Watch(listOptions) + if err != nil { + return nil, errors.WithStack(err) + } + defer watcher.Stop() + + expired := time.NewTimer(g.timeout) + defer expired.Stop() + +Loop: + for { + select { + case <-expired.C: + return nil, errors.New("timed out waiting for server status request to be processed") + case e := <-watcher.ResultChan(): + updated, ok := e.Object.(*arkv1api.ServerStatusRequest) + if !ok { + return nil, errors.Errorf("unexpected type %T", e.Object) + } + + // TODO: once the minimum supported Kubernetes version is v1.9.0, remove the following check. + // See http://issue.k8s.io/51046 for details. + if updated.Name != created.Name { + continue + } + + switch e.Type { + case watch.Deleted: + return nil, errors.New("server status request was unexpectedly deleted") + case watch.Modified: + if updated.Status.Phase == arkv1api.ServerStatusRequestPhaseProcessed { + req = updated + break Loop + } + } + } + } + + return req, nil +} diff --git a/pkg/cmd/version/version_test.go b/pkg/cmd/version/version_test.go new file mode 100644 index 000000000..960393fde --- /dev/null +++ b/pkg/cmd/version/version_test.go @@ -0,0 +1,129 @@ +/* +Copyright 2019 the Heptio Ark contributors. + +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 version + +import ( + "bytes" + "fmt" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + arkv1 "github.com/heptio/ark/pkg/apis/ark/v1" + "github.com/heptio/ark/pkg/buildinfo" + "github.com/heptio/ark/pkg/generated/clientset/versioned/fake" + v1 "github.com/heptio/ark/pkg/generated/clientset/versioned/typed/ark/v1" + "github.com/heptio/ark/pkg/serverstatusrequest" +) + +func TestPrintVersion(t *testing.T) { + // set up some non-empty buildinfo values, but put them back to their + // defaults at the end of the test + var ( + origVersion = buildinfo.Version + origGitSHA = buildinfo.GitSHA + origGitTreeState = buildinfo.GitTreeState + ) + defer func() { + buildinfo.Version = origVersion + buildinfo.GitSHA = origGitSHA + buildinfo.GitTreeState = origGitTreeState + }() + buildinfo.Version = "v1.0.0" + buildinfo.GitSHA = "somegitsha" + buildinfo.GitTreeState = "dirty" + + clientVersion := fmt.Sprintf("Client:\n\tVersion: %s\n\tGit commit: %s\n", buildinfo.Version, buildinfo.FormattedGitSHA()) + + tests := []struct { + name string + clientOnly bool + serverStatusRequest *arkv1.ServerStatusRequest + getterError error + want string + }{ + { + name: "client-only", + clientOnly: true, + want: clientVersion, + }, + { + name: "server status getter error", + clientOnly: false, + serverStatusRequest: nil, + getterError: errors.New("an error"), + want: clientVersion + "\n", + }, + { + name: "server status getter returns normally", + clientOnly: false, + serverStatusRequest: serverstatusrequest.NewBuilder().ServerVersion("v1.0.1").Build(), + getterError: nil, + want: clientVersion + "Server:\n\tVersion: v1.0.1\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var ( + serverStatusGetter = new(mockServerStatusGetter) + buf = new(bytes.Buffer) + client = fake.NewSimpleClientset() + ) + defer serverStatusGetter.AssertExpectations(t) + + // getServerStatus should only be called when clientOnly = false + if !tc.clientOnly { + serverStatusGetter.On("getServerStatus", client.ArkV1()).Return(tc.serverStatusRequest, tc.getterError) + } + + printVersion(buf, tc.clientOnly, client.ArkV1(), serverStatusGetter) + + assert.Equal(t, tc.want, buf.String()) + }) + } +} + +// serverStatusGetter is an autogenerated mock type for the serverStatusGetter type +type mockServerStatusGetter struct { + mock.Mock +} + +// getServerStatus provides a mock function with given fields: client +func (_m *mockServerStatusGetter) getServerStatus(client v1.ServerStatusRequestsGetter) (*arkv1.ServerStatusRequest, error) { + ret := _m.Called(client) + + var r0 *arkv1.ServerStatusRequest + if rf, ok := ret.Get(0).(func(v1.ServerStatusRequestsGetter) *arkv1.ServerStatusRequest); ok { + r0 = rf(client) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*arkv1.ServerStatusRequest) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(v1.ServerStatusRequestsGetter) error); ok { + r1 = rf(client) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/controller/server_status_request_controller.go b/pkg/controller/server_status_request_controller.go new file mode 100644 index 000000000..be3150d07 --- /dev/null +++ b/pkg/controller/server_status_request_controller.go @@ -0,0 +1,118 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 controller + +import ( + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/clock" + "k8s.io/client-go/tools/cache" + + arkv1api "github.com/heptio/ark/pkg/apis/ark/v1" + arkv1client "github.com/heptio/ark/pkg/generated/clientset/versioned/typed/ark/v1" + arkv1informers "github.com/heptio/ark/pkg/generated/informers/externalversions/ark/v1" + arkv1listers "github.com/heptio/ark/pkg/generated/listers/ark/v1" + "github.com/heptio/ark/pkg/serverstatusrequest" + kubeutil "github.com/heptio/ark/pkg/util/kube" +) + +const statusRequestResyncPeriod = 5 * time.Minute + +type statusRequestController struct { + *genericController + + client arkv1client.ServerStatusRequestsGetter + lister arkv1listers.ServerStatusRequestLister + clock clock.Clock +} + +func NewServerStatusRequestController( + logger logrus.FieldLogger, + client arkv1client.ServerStatusRequestsGetter, + informer arkv1informers.ServerStatusRequestInformer, +) *statusRequestController { + c := &statusRequestController{ + genericController: newGenericController("serverstatusrequest", logger), + client: client, + lister: informer.Lister(), + + clock: clock.RealClock{}, + } + + c.syncHandler = c.processItem + c.cacheSyncWaiters = append(c.cacheSyncWaiters, informer.Informer().HasSynced) + + c.resyncFunc = c.enqueueAllItems + c.resyncPeriod = statusRequestResyncPeriod + + informer.Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + req := obj.(*arkv1api.ServerStatusRequest) + key := kubeutil.NamespaceAndName(req) + + c.logger.WithFields(logrus.Fields{ + "serverStatusRequest": key, + "phase": req.Status.Phase, + }).Debug("Enqueueing server status request") + + c.queue.Add(key) + }, + }, + ) + + return c +} + +func (c *statusRequestController) processItem(key string) error { + log := c.logger.WithField("key", key) + + log.Debug("Running processItem") + ns, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return errors.Wrap(err, "error splitting queue key") + } + + log.Debug("Getting ServerStatusRequest") + req, err := c.lister.ServerStatusRequests(ns).Get(name) + // server status request no longer exists + if apierrors.IsNotFound(err) { + log.WithError(err).Debug("ServerStatusRequest not found") + return nil + } + if err != nil { + return errors.Wrap(err, "error getting ServerStatusRequest") + } + + return serverstatusrequest.Process(req.DeepCopy(), c.client, c.clock, log) +} + +func (c *statusRequestController) enqueueAllItems() { + items, err := c.lister.List(labels.Everything()) + if err != nil { + c.logger.WithError(err).Error("Error listing all server status requests") + return + } + + for _, req := range items { + c.enqueue(req) + } +} diff --git a/pkg/generated/clientset/versioned/typed/ark/v1/ark_client.go b/pkg/generated/clientset/versioned/typed/ark/v1/ark_client.go index 07d84f75d..b638a900a 100644 --- a/pkg/generated/clientset/versioned/typed/ark/v1/ark_client.go +++ b/pkg/generated/clientset/versioned/typed/ark/v1/ark_client.go @@ -36,6 +36,7 @@ type ArkV1Interface interface { ResticRepositoriesGetter RestoresGetter SchedulesGetter + ServerStatusRequestsGetter VolumeSnapshotLocationsGetter } @@ -80,6 +81,10 @@ func (c *ArkV1Client) Schedules(namespace string) ScheduleInterface { return newSchedules(c, namespace) } +func (c *ArkV1Client) ServerStatusRequests(namespace string) ServerStatusRequestInterface { + return newServerStatusRequests(c, namespace) +} + func (c *ArkV1Client) VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationInterface { return newVolumeSnapshotLocations(c, namespace) } diff --git a/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_ark_client.go b/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_ark_client.go index 91708f38b..ebf5461db 100644 --- a/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_ark_client.go +++ b/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_ark_client.go @@ -64,6 +64,10 @@ func (c *FakeArkV1) Schedules(namespace string) v1.ScheduleInterface { return &FakeSchedules{c, namespace} } +func (c *FakeArkV1) ServerStatusRequests(namespace string) v1.ServerStatusRequestInterface { + return &FakeServerStatusRequests{c, namespace} +} + func (c *FakeArkV1) VolumeSnapshotLocations(namespace string) v1.VolumeSnapshotLocationInterface { return &FakeVolumeSnapshotLocations{c, namespace} } diff --git a/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_serverstatusrequest.go b/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_serverstatusrequest.go new file mode 100644 index 000000000..a4c3caacb --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/ark/v1/fake/fake_serverstatusrequest.go @@ -0,0 +1,140 @@ +/* +Copyright the Heptio Ark contributors. + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + ark_v1 "github.com/heptio/ark/pkg/apis/ark/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeServerStatusRequests implements ServerStatusRequestInterface +type FakeServerStatusRequests struct { + Fake *FakeArkV1 + ns string +} + +var serverstatusrequestsResource = schema.GroupVersionResource{Group: "ark.heptio.com", Version: "v1", Resource: "serverstatusrequests"} + +var serverstatusrequestsKind = schema.GroupVersionKind{Group: "ark.heptio.com", Version: "v1", Kind: "ServerStatusRequest"} + +// Get takes name of the serverStatusRequest, and returns the corresponding serverStatusRequest object, and an error if there is any. +func (c *FakeServerStatusRequests) Get(name string, options v1.GetOptions) (result *ark_v1.ServerStatusRequest, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(serverstatusrequestsResource, c.ns, name), &ark_v1.ServerStatusRequest{}) + + if obj == nil { + return nil, err + } + return obj.(*ark_v1.ServerStatusRequest), err +} + +// List takes label and field selectors, and returns the list of ServerStatusRequests that match those selectors. +func (c *FakeServerStatusRequests) List(opts v1.ListOptions) (result *ark_v1.ServerStatusRequestList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(serverstatusrequestsResource, serverstatusrequestsKind, c.ns, opts), &ark_v1.ServerStatusRequestList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &ark_v1.ServerStatusRequestList{ListMeta: obj.(*ark_v1.ServerStatusRequestList).ListMeta} + for _, item := range obj.(*ark_v1.ServerStatusRequestList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested serverStatusRequests. +func (c *FakeServerStatusRequests) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(serverstatusrequestsResource, c.ns, opts)) + +} + +// Create takes the representation of a serverStatusRequest and creates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. +func (c *FakeServerStatusRequests) Create(serverStatusRequest *ark_v1.ServerStatusRequest) (result *ark_v1.ServerStatusRequest, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(serverstatusrequestsResource, c.ns, serverStatusRequest), &ark_v1.ServerStatusRequest{}) + + if obj == nil { + return nil, err + } + return obj.(*ark_v1.ServerStatusRequest), err +} + +// Update takes the representation of a serverStatusRequest and updates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. +func (c *FakeServerStatusRequests) Update(serverStatusRequest *ark_v1.ServerStatusRequest) (result *ark_v1.ServerStatusRequest, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(serverstatusrequestsResource, c.ns, serverStatusRequest), &ark_v1.ServerStatusRequest{}) + + if obj == nil { + return nil, err + } + return obj.(*ark_v1.ServerStatusRequest), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeServerStatusRequests) UpdateStatus(serverStatusRequest *ark_v1.ServerStatusRequest) (*ark_v1.ServerStatusRequest, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(serverstatusrequestsResource, "status", c.ns, serverStatusRequest), &ark_v1.ServerStatusRequest{}) + + if obj == nil { + return nil, err + } + return obj.(*ark_v1.ServerStatusRequest), err +} + +// Delete takes name of the serverStatusRequest and deletes it. Returns an error if one occurs. +func (c *FakeServerStatusRequests) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(serverstatusrequestsResource, c.ns, name), &ark_v1.ServerStatusRequest{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeServerStatusRequests) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(serverstatusrequestsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &ark_v1.ServerStatusRequestList{}) + return err +} + +// Patch applies the patch and returns the patched serverStatusRequest. +func (c *FakeServerStatusRequests) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *ark_v1.ServerStatusRequest, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(serverstatusrequestsResource, c.ns, name, data, subresources...), &ark_v1.ServerStatusRequest{}) + + if obj == nil { + return nil, err + } + return obj.(*ark_v1.ServerStatusRequest), err +} diff --git a/pkg/generated/clientset/versioned/typed/ark/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/ark/v1/generated_expansion.go index 3c5a7911b..9c0a18a9d 100644 --- a/pkg/generated/clientset/versioned/typed/ark/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/ark/v1/generated_expansion.go @@ -36,4 +36,6 @@ type RestoreExpansion interface{} type ScheduleExpansion interface{} +type ServerStatusRequestExpansion interface{} + type VolumeSnapshotLocationExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/ark/v1/serverstatusrequest.go b/pkg/generated/clientset/versioned/typed/ark/v1/serverstatusrequest.go new file mode 100644 index 000000000..c232952c0 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/ark/v1/serverstatusrequest.go @@ -0,0 +1,174 @@ +/* +Copyright the Heptio Ark contributors. + +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/heptio/ark/pkg/apis/ark/v1" + scheme "github.com/heptio/ark/pkg/generated/clientset/versioned/scheme" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ServerStatusRequestsGetter has a method to return a ServerStatusRequestInterface. +// A group's client should implement this interface. +type ServerStatusRequestsGetter interface { + ServerStatusRequests(namespace string) ServerStatusRequestInterface +} + +// ServerStatusRequestInterface has methods to work with ServerStatusRequest resources. +type ServerStatusRequestInterface interface { + Create(*v1.ServerStatusRequest) (*v1.ServerStatusRequest, error) + Update(*v1.ServerStatusRequest) (*v1.ServerStatusRequest, error) + UpdateStatus(*v1.ServerStatusRequest) (*v1.ServerStatusRequest, error) + Delete(name string, options *meta_v1.DeleteOptions) error + DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error + Get(name string, options meta_v1.GetOptions) (*v1.ServerStatusRequest, error) + List(opts meta_v1.ListOptions) (*v1.ServerStatusRequestList, error) + Watch(opts meta_v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ServerStatusRequest, err error) + ServerStatusRequestExpansion +} + +// serverStatusRequests implements ServerStatusRequestInterface +type serverStatusRequests struct { + client rest.Interface + ns string +} + +// newServerStatusRequests returns a ServerStatusRequests +func newServerStatusRequests(c *ArkV1Client, namespace string) *serverStatusRequests { + return &serverStatusRequests{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the serverStatusRequest, and returns the corresponding serverStatusRequest object, and an error if there is any. +func (c *serverStatusRequests) Get(name string, options meta_v1.GetOptions) (result *v1.ServerStatusRequest, err error) { + result = &v1.ServerStatusRequest{} + err = c.client.Get(). + Namespace(c.ns). + Resource("serverstatusrequests"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ServerStatusRequests that match those selectors. +func (c *serverStatusRequests) List(opts meta_v1.ListOptions) (result *v1.ServerStatusRequestList, err error) { + result = &v1.ServerStatusRequestList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("serverstatusrequests"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested serverStatusRequests. +func (c *serverStatusRequests) Watch(opts meta_v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("serverstatusrequests"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a serverStatusRequest and creates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. +func (c *serverStatusRequests) Create(serverStatusRequest *v1.ServerStatusRequest) (result *v1.ServerStatusRequest, err error) { + result = &v1.ServerStatusRequest{} + err = c.client.Post(). + Namespace(c.ns). + Resource("serverstatusrequests"). + Body(serverStatusRequest). + Do(). + Into(result) + return +} + +// Update takes the representation of a serverStatusRequest and updates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. +func (c *serverStatusRequests) Update(serverStatusRequest *v1.ServerStatusRequest) (result *v1.ServerStatusRequest, err error) { + result = &v1.ServerStatusRequest{} + err = c.client.Put(). + Namespace(c.ns). + Resource("serverstatusrequests"). + Name(serverStatusRequest.Name). + Body(serverStatusRequest). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *serverStatusRequests) UpdateStatus(serverStatusRequest *v1.ServerStatusRequest) (result *v1.ServerStatusRequest, err error) { + result = &v1.ServerStatusRequest{} + err = c.client.Put(). + Namespace(c.ns). + Resource("serverstatusrequests"). + Name(serverStatusRequest.Name). + SubResource("status"). + Body(serverStatusRequest). + Do(). + Into(result) + return +} + +// Delete takes name of the serverStatusRequest and deletes it. Returns an error if one occurs. +func (c *serverStatusRequests) Delete(name string, options *meta_v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("serverstatusrequests"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *serverStatusRequests) DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("serverstatusrequests"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched serverStatusRequest. +func (c *serverStatusRequests) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ServerStatusRequest, err error) { + result = &v1.ServerStatusRequest{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("serverstatusrequests"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/generated/informers/externalversions/ark/v1/interface.go b/pkg/generated/informers/externalversions/ark/v1/interface.go index 354ccb0b3..8c569d145 100644 --- a/pkg/generated/informers/externalversions/ark/v1/interface.go +++ b/pkg/generated/informers/externalversions/ark/v1/interface.go @@ -42,6 +42,8 @@ type Interface interface { Restores() RestoreInformer // Schedules returns a ScheduleInformer. Schedules() ScheduleInformer + // ServerStatusRequests returns a ServerStatusRequestInformer. + ServerStatusRequests() ServerStatusRequestInformer // VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer. VolumeSnapshotLocations() VolumeSnapshotLocationInformer } @@ -102,6 +104,11 @@ func (v *version) Schedules() ScheduleInformer { return &scheduleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// ServerStatusRequests returns a ServerStatusRequestInformer. +func (v *version) ServerStatusRequests() ServerStatusRequestInformer { + return &serverStatusRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer. func (v *version) VolumeSnapshotLocations() VolumeSnapshotLocationInformer { return &volumeSnapshotLocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/generated/informers/externalversions/ark/v1/serverstatusrequest.go b/pkg/generated/informers/externalversions/ark/v1/serverstatusrequest.go new file mode 100644 index 000000000..7e8f1529a --- /dev/null +++ b/pkg/generated/informers/externalversions/ark/v1/serverstatusrequest.go @@ -0,0 +1,89 @@ +/* +Copyright the Heptio Ark contributors. + +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + time "time" + + ark_v1 "github.com/heptio/ark/pkg/apis/ark/v1" + versioned "github.com/heptio/ark/pkg/generated/clientset/versioned" + internalinterfaces "github.com/heptio/ark/pkg/generated/informers/externalversions/internalinterfaces" + v1 "github.com/heptio/ark/pkg/generated/listers/ark/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ServerStatusRequestInformer provides access to a shared informer and lister for +// ServerStatusRequests. +type ServerStatusRequestInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.ServerStatusRequestLister +} + +type serverStatusRequestInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewServerStatusRequestInformer constructs a new informer for ServerStatusRequest type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewServerStatusRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredServerStatusRequestInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredServerStatusRequestInformer constructs a new informer for ServerStatusRequest type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredServerStatusRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options meta_v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ArkV1().ServerStatusRequests(namespace).List(options) + }, + WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ArkV1().ServerStatusRequests(namespace).Watch(options) + }, + }, + &ark_v1.ServerStatusRequest{}, + resyncPeriod, + indexers, + ) +} + +func (f *serverStatusRequestInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredServerStatusRequestInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *serverStatusRequestInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&ark_v1.ServerStatusRequest{}, f.defaultInformer) +} + +func (f *serverStatusRequestInformer) Lister() v1.ServerStatusRequestLister { + return v1.NewServerStatusRequestLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index b0acd485c..349cc61e5 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -71,6 +71,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().Restores().Informer()}, nil case v1.SchemeGroupVersion.WithResource("schedules"): return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().Schedules().Informer()}, nil + case v1.SchemeGroupVersion.WithResource("serverstatusrequests"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().ServerStatusRequests().Informer()}, nil case v1.SchemeGroupVersion.WithResource("volumesnapshotlocations"): return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().VolumeSnapshotLocations().Informer()}, nil diff --git a/pkg/generated/listers/ark/v1/expansion_generated.go b/pkg/generated/listers/ark/v1/expansion_generated.go index 9f9d08d22..018228095 100644 --- a/pkg/generated/listers/ark/v1/expansion_generated.go +++ b/pkg/generated/listers/ark/v1/expansion_generated.go @@ -90,6 +90,14 @@ type ScheduleListerExpansion interface{} // ScheduleNamespaceLister. type ScheduleNamespaceListerExpansion interface{} +// ServerStatusRequestListerExpansion allows custom methods to be added to +// ServerStatusRequestLister. +type ServerStatusRequestListerExpansion interface{} + +// ServerStatusRequestNamespaceListerExpansion allows custom methods to be added to +// ServerStatusRequestNamespaceLister. +type ServerStatusRequestNamespaceListerExpansion interface{} + // VolumeSnapshotLocationListerExpansion allows custom methods to be added to // VolumeSnapshotLocationLister. type VolumeSnapshotLocationListerExpansion interface{} diff --git a/pkg/generated/listers/ark/v1/serverstatusrequest.go b/pkg/generated/listers/ark/v1/serverstatusrequest.go new file mode 100644 index 000000000..ec2851acd --- /dev/null +++ b/pkg/generated/listers/ark/v1/serverstatusrequest.go @@ -0,0 +1,94 @@ +/* +Copyright the Heptio Ark contributors. + +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/heptio/ark/pkg/apis/ark/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ServerStatusRequestLister helps list ServerStatusRequests. +type ServerStatusRequestLister interface { + // List lists all ServerStatusRequests in the indexer. + List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) + // ServerStatusRequests returns an object that can list and get ServerStatusRequests. + ServerStatusRequests(namespace string) ServerStatusRequestNamespaceLister + ServerStatusRequestListerExpansion +} + +// serverStatusRequestLister implements the ServerStatusRequestLister interface. +type serverStatusRequestLister struct { + indexer cache.Indexer +} + +// NewServerStatusRequestLister returns a new ServerStatusRequestLister. +func NewServerStatusRequestLister(indexer cache.Indexer) ServerStatusRequestLister { + return &serverStatusRequestLister{indexer: indexer} +} + +// List lists all ServerStatusRequests in the indexer. +func (s *serverStatusRequestLister) List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.ServerStatusRequest)) + }) + return ret, err +} + +// ServerStatusRequests returns an object that can list and get ServerStatusRequests. +func (s *serverStatusRequestLister) ServerStatusRequests(namespace string) ServerStatusRequestNamespaceLister { + return serverStatusRequestNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ServerStatusRequestNamespaceLister helps list and get ServerStatusRequests. +type ServerStatusRequestNamespaceLister interface { + // List lists all ServerStatusRequests in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) + // Get retrieves the ServerStatusRequest from the indexer for a given namespace and name. + Get(name string) (*v1.ServerStatusRequest, error) + ServerStatusRequestNamespaceListerExpansion +} + +// serverStatusRequestNamespaceLister implements the ServerStatusRequestNamespaceLister +// interface. +type serverStatusRequestNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ServerStatusRequests in the indexer for a given namespace. +func (s serverStatusRequestNamespaceLister) List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.ServerStatusRequest)) + }) + return ret, err +} + +// Get retrieves the ServerStatusRequest from the indexer for a given namespace and name. +func (s serverStatusRequestNamespaceLister) Get(name string) (*v1.ServerStatusRequest, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("serverstatusrequest"), name) + } + return obj.(*v1.ServerStatusRequest), nil +} diff --git a/pkg/serverstatusrequest/builder.go b/pkg/serverstatusrequest/builder.go new file mode 100644 index 000000000..31c14918e --- /dev/null +++ b/pkg/serverstatusrequest/builder.go @@ -0,0 +1,74 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 serverstatusrequest + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + arkv1api "github.com/heptio/ark/pkg/apis/ark/v1" +) + +type Builder struct { + serverStatusRequest arkv1api.ServerStatusRequest +} + +func NewBuilder() *Builder { + return &Builder{ + serverStatusRequest: arkv1api.ServerStatusRequest{ + TypeMeta: metav1.TypeMeta{ + APIVersion: arkv1api.SchemeGroupVersion.String(), + Kind: "ServerStatusRequest", + }, + }, + } +} + +func (b *Builder) Build() *arkv1api.ServerStatusRequest { + return &b.serverStatusRequest +} + +func (b *Builder) Namespace(namespace string) *Builder { + b.serverStatusRequest.Namespace = namespace + return b +} + +func (b *Builder) Name(name string) *Builder { + b.serverStatusRequest.Name = name + return b +} + +func (b *Builder) GenerateName(name string) *Builder { + b.serverStatusRequest.GenerateName = name + return b +} + +func (b *Builder) Phase(phase arkv1api.ServerStatusRequestPhase) *Builder { + b.serverStatusRequest.Status.Phase = phase + return b +} + +func (b *Builder) ProcessedTimestamp(time time.Time) *Builder { + b.serverStatusRequest.Status.ProcessedTimestamp.Time = time + return b +} + +func (b *Builder) ServerVersion(version string) *Builder { + b.serverStatusRequest.Status.ServerVersion = version + return b +} diff --git a/pkg/serverstatusrequest/process.go b/pkg/serverstatusrequest/process.go new file mode 100644 index 000000000..91c3df46a --- /dev/null +++ b/pkg/serverstatusrequest/process.go @@ -0,0 +1,90 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 serverstatusrequest + +import ( + "encoding/json" + "time" + + jsonpatch "github.com/evanphx/json-patch" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + + arkv1api "github.com/heptio/ark/pkg/apis/ark/v1" + "github.com/heptio/ark/pkg/buildinfo" + arkv1client "github.com/heptio/ark/pkg/generated/clientset/versioned/typed/ark/v1" +) + +const ttl = time.Minute + +// Process fills out new ServerStatusRequest objects and deletes processed ones +// that have expired. +func Process(req *arkv1api.ServerStatusRequest, client arkv1client.ServerStatusRequestsGetter, clock clock.Clock, log logrus.FieldLogger) error { + switch req.Status.Phase { + case "", arkv1api.ServerStatusRequestPhaseNew: + log.Info("Processing new ServerStatusRequest") + return errors.WithStack(patch(client, req, func(req *arkv1api.ServerStatusRequest) { + req.Status.ServerVersion = buildinfo.Version + req.Status.ProcessedTimestamp.Time = clock.Now() + req.Status.Phase = arkv1api.ServerStatusRequestPhaseProcessed + })) + case arkv1api.ServerStatusRequestPhaseProcessed: + log.Debug("Checking whether ServerStatusRequest has expired") + expiration := req.Status.ProcessedTimestamp.Add(ttl) + if expiration.After(clock.Now()) { + log.Debug("ServerStatusRequest has not expired") + return nil + } + + log.Debug("ServerStatusRequest has expired, deleting it") + if err := client.ServerStatusRequests(req.Namespace).Delete(req.Name, nil); err != nil { + return errors.WithStack(err) + } + + return nil + default: + return errors.Errorf("unexpected ServerStatusRequest phase %q", req.Status.Phase) + } +} + +func patch(client arkv1client.ServerStatusRequestsGetter, req *arkv1api.ServerStatusRequest, updateFunc func(*arkv1api.ServerStatusRequest)) error { + originalJSON, err := json.Marshal(req) + if err != nil { + return errors.WithStack(err) + } + + updateFunc(req) + + updatedJSON, err := json.Marshal(req) + if err != nil { + return errors.WithStack(err) + } + + patchBytes, err := jsonpatch.CreateMergePatch(originalJSON, updatedJSON) + if err != nil { + return errors.WithStack(err) + } + + _, err = client.ServerStatusRequests(req.Namespace).Patch(req.Name, types.MergePatchType, patchBytes) + if err != nil { + return errors.WithStack(err) + } + + return nil +} diff --git a/pkg/serverstatusrequest/process_test.go b/pkg/serverstatusrequest/process_test.go new file mode 100644 index 000000000..6f625b6bd --- /dev/null +++ b/pkg/serverstatusrequest/process_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 serverstatusrequest + +import ( + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/clock" + + arkv1api "github.com/heptio/ark/pkg/apis/ark/v1" + "github.com/heptio/ark/pkg/buildinfo" + "github.com/heptio/ark/pkg/generated/clientset/versioned/fake" +) + +func statusRequestBuilder() *Builder { + return NewBuilder().Namespace(arkv1api.DefaultNamespace).Name("sr-1") +} + +func TestProcess(t *testing.T) { + // now will be used to set the fake clock's time; capture + // it here so it can be referenced in the test case defs. + now, err := time.Parse(time.RFC1123, time.RFC1123) + require.NoError(t, err) + now = now.Local() + + buildinfo.Version = "test-version-val" + + tests := []struct { + name string + req *arkv1api.ServerStatusRequest + expected *arkv1api.ServerStatusRequest + expectedErrMsg string + }{ + { + name: "server status request with empty phase gets processed", + req: statusRequestBuilder().Build(), + expected: statusRequestBuilder(). + ServerVersion(buildinfo.Version). + Phase(arkv1api.ServerStatusRequestPhaseProcessed). + ProcessedTimestamp(now). + Build(), + }, + { + name: "server status request with phase=New gets processed", + req: statusRequestBuilder(). + Phase(arkv1api.ServerStatusRequestPhaseNew). + Build(), + expected: statusRequestBuilder(). + ServerVersion(buildinfo.Version). + Phase(arkv1api.ServerStatusRequestPhaseProcessed). + ProcessedTimestamp(now). + Build(), + }, + { + name: "server status request with phase=Processed gets deleted if expired", + req: statusRequestBuilder(). + Phase(arkv1api.ServerStatusRequestPhaseProcessed). + ProcessedTimestamp(now.Add(-61 * time.Second)). + Build(), + expected: nil, + }, + { + name: "server status request with phase=Processed does not get deleted if not expired", + req: statusRequestBuilder(). + Phase(arkv1api.ServerStatusRequestPhaseProcessed). + ProcessedTimestamp(now.Add(-59 * time.Second)). + Build(), + expected: statusRequestBuilder(). + Phase(arkv1api.ServerStatusRequestPhaseProcessed). + ProcessedTimestamp(now.Add(-59 * time.Second)). + Build(), + }, + { + name: "server status request with invalid phase returns an error", + req: statusRequestBuilder(). + Phase(arkv1api.ServerStatusRequestPhase("an-invalid-phase")). + Build(), + expected: statusRequestBuilder(). + Phase(arkv1api.ServerStatusRequestPhase("an-invalid-phase")). + Build(), + expectedErrMsg: "unexpected ServerStatusRequest phase \"an-invalid-phase\"", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + client := fake.NewSimpleClientset(tc.req) + + err := Process(tc.req, client.ArkV1(), clock.NewFakeClock(now), logrus.StandardLogger()) + if tc.expectedErrMsg == "" { + assert.Nil(t, err) + } else { + assert.EqualError(t, err, tc.expectedErrMsg) + } + + res, err := client.ArkV1().ServerStatusRequests(tc.req.Namespace).Get(tc.req.Name, metav1.GetOptions{}) + if tc.expected == nil { + assert.Nil(t, res) + assert.True(t, apierrors.IsNotFound(err)) + } else { + assert.Equal(t, tc.expected, res) + assert.Nil(t, err) + } + }) + } +}