2018-05-13 13:28:09 +00:00
/ *
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 .
* /
2018-06-08 18:03:07 +00:00
package restic
2018-02-28 01:35:35 +00:00
import (
"context"
"fmt"
"os"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeinformers "k8s.io/client-go/informers"
corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"github.com/heptio/ark/pkg/buildinfo"
"github.com/heptio/ark/pkg/client"
"github.com/heptio/ark/pkg/cmd"
"github.com/heptio/ark/pkg/cmd/util/signals"
"github.com/heptio/ark/pkg/controller"
clientset "github.com/heptio/ark/pkg/generated/clientset/versioned"
informers "github.com/heptio/ark/pkg/generated/informers/externalversions"
2018-06-22 19:07:23 +00:00
"github.com/heptio/ark/pkg/restic"
2018-02-28 01:35:35 +00:00
"github.com/heptio/ark/pkg/util/logging"
)
2018-06-08 18:03:07 +00:00
func NewServerCommand ( f client . Factory ) * cobra . Command {
2018-09-25 21:46:29 +00:00
logLevelFlag := logging . LogLevelFlag ( logrus . InfoLevel )
2018-02-28 01:35:35 +00:00
2018-09-25 21:46:29 +00:00
command := & cobra . Command {
2018-06-08 18:03:07 +00:00
Use : "server" ,
Short : "Run the ark restic server" ,
Long : "Run the ark restic server" ,
2018-02-28 01:35:35 +00:00
Run : func ( c * cobra . Command , args [ ] string ) {
logLevel := logLevelFlag . Parse ( )
2018-06-08 18:03:07 +00:00
logrus . Infof ( "Setting log-level to %s" , strings . ToUpper ( logLevel . String ( ) ) )
2018-02-28 01:35:35 +00:00
logger := logging . DefaultLogger ( logLevel )
2018-06-08 18:03:07 +00:00
logger . Infof ( "Starting Ark restic server %s" , buildinfo . FormattedGitSHA ( ) )
2018-02-28 01:35:35 +00:00
2018-09-25 21:46:29 +00:00
s , err := newResticServer ( logger , fmt . Sprintf ( "%s-%s" , c . Parent ( ) . Name ( ) , c . Name ( ) ) )
2018-02-28 01:35:35 +00:00
cmd . CheckError ( err )
s . run ( )
} ,
}
command . Flags ( ) . Var ( logLevelFlag , "log-level" , fmt . Sprintf ( "the level at which to log. Valid values are %s." , strings . Join ( logLevelFlag . AllowedValues ( ) , ", " ) ) )
return command
}
2018-06-08 18:03:07 +00:00
type resticServer struct {
2018-02-28 01:35:35 +00:00
kubeClient kubernetes . Interface
arkClient clientset . Interface
arkInformerFactory informers . SharedInformerFactory
kubeInformerFactory kubeinformers . SharedInformerFactory
podInformer cache . SharedIndexInformer
2018-06-22 19:07:23 +00:00
secretInformer cache . SharedIndexInformer
2018-02-28 01:35:35 +00:00
logger logrus . FieldLogger
ctx context . Context
cancelFunc context . CancelFunc
}
2018-09-25 21:46:29 +00:00
func newResticServer ( logger logrus . FieldLogger , baseName string ) ( * resticServer , error ) {
2018-02-28 01:35:35 +00:00
clientConfig , err := client . Config ( "" , "" , baseName )
if err != nil {
return nil , err
}
kubeClient , err := kubernetes . NewForConfig ( clientConfig )
if err != nil {
return nil , errors . WithStack ( err )
}
arkClient , err := clientset . NewForConfig ( clientConfig )
if err != nil {
return nil , errors . WithStack ( err )
}
// use a stand-alone pod informer because we want to use a field selector to
// filter to only pods scheduled on this node.
podInformer := corev1informers . NewFilteredPodInformer (
kubeClient ,
2018-06-22 19:07:23 +00:00
metav1 . NamespaceAll ,
2018-02-28 01:35:35 +00:00
0 ,
cache . Indexers { cache . NamespaceIndex : cache . MetaNamespaceIndexFunc } ,
func ( opts * metav1 . ListOptions ) {
opts . FieldSelector = fmt . Sprintf ( "spec.nodeName=%s" , os . Getenv ( "NODE_NAME" ) )
} ,
)
2018-06-22 19:07:23 +00:00
// use a stand-alone secrets informer so we can filter to only the restic credentials
// secret(s) within the heptio-ark namespace
//
// note: using an informer to access the single secret for all ark-managed
// restic repositories is overkill for now, but will be useful when we move
// to fully-encrypted backups and have unique keys per repository.
secretInformer := corev1informers . NewFilteredSecretInformer (
kubeClient ,
os . Getenv ( "HEPTIO_ARK_NAMESPACE" ) ,
0 ,
cache . Indexers { cache . NamespaceIndex : cache . MetaNamespaceIndexFunc } ,
func ( opts * metav1 . ListOptions ) {
opts . FieldSelector = fmt . Sprintf ( "metadata.name=%s" , restic . CredentialsSecretName )
} ,
)
2018-02-28 01:35:35 +00:00
ctx , cancelFunc := context . WithCancel ( context . Background ( ) )
2018-06-08 18:03:07 +00:00
return & resticServer {
2018-02-28 01:35:35 +00:00
kubeClient : kubeClient ,
arkClient : arkClient ,
arkInformerFactory : informers . NewFilteredSharedInformerFactory ( arkClient , 0 , os . Getenv ( "HEPTIO_ARK_NAMESPACE" ) , nil ) ,
kubeInformerFactory : kubeinformers . NewSharedInformerFactory ( kubeClient , 0 ) ,
podInformer : podInformer ,
2018-06-22 19:07:23 +00:00
secretInformer : secretInformer ,
2018-02-28 01:35:35 +00:00
logger : logger ,
ctx : ctx ,
cancelFunc : cancelFunc ,
} , nil
}
2018-06-08 18:03:07 +00:00
func ( s * resticServer ) run ( ) {
2018-02-28 01:35:35 +00:00
signals . CancelOnShutdown ( s . cancelFunc , s . logger )
s . logger . Info ( "Starting controllers" )
var wg sync . WaitGroup
backupController := controller . NewPodVolumeBackupController (
s . logger ,
s . arkInformerFactory . Ark ( ) . V1 ( ) . PodVolumeBackups ( ) ,
s . arkClient . ArkV1 ( ) ,
s . podInformer ,
2018-06-22 19:07:23 +00:00
s . secretInformer ,
2018-02-28 01:35:35 +00:00
s . kubeInformerFactory . Core ( ) . V1 ( ) . PersistentVolumeClaims ( ) ,
2018-09-25 21:46:29 +00:00
s . arkInformerFactory . Ark ( ) . V1 ( ) . BackupStorageLocations ( ) ,
2018-02-28 01:35:35 +00:00
os . Getenv ( "NODE_NAME" ) ,
)
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
backupController . Run ( s . ctx , 1 )
} ( )
restoreController := controller . NewPodVolumeRestoreController (
s . logger ,
s . arkInformerFactory . Ark ( ) . V1 ( ) . PodVolumeRestores ( ) ,
s . arkClient . ArkV1 ( ) ,
s . podInformer ,
2018-06-22 19:07:23 +00:00
s . secretInformer ,
2018-02-28 01:35:35 +00:00
s . kubeInformerFactory . Core ( ) . V1 ( ) . PersistentVolumeClaims ( ) ,
2018-09-25 21:46:29 +00:00
s . arkInformerFactory . Ark ( ) . V1 ( ) . BackupStorageLocations ( ) ,
2018-02-28 01:35:35 +00:00
os . Getenv ( "NODE_NAME" ) ,
)
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
restoreController . Run ( s . ctx , 1 )
} ( )
go s . arkInformerFactory . Start ( s . ctx . Done ( ) )
go s . kubeInformerFactory . Start ( s . ctx . Done ( ) )
go s . podInformer . Run ( s . ctx . Done ( ) )
2018-06-22 19:07:23 +00:00
go s . secretInformer . Run ( s . ctx . Done ( ) )
2018-02-28 01:35:35 +00:00
s . logger . Info ( "Controllers started successfully" )
<- s . ctx . Done ( )
s . logger . Info ( "Waiting for all controllers to shut down gracefully" )
wg . Wait ( )
}