├── .gitattributes ├── .gitignore ├── License.md ├── README.md ├── ServiceFabric.BackupRestore.sln ├── demo ├── MyStatefulService │ ├── App.config │ ├── MyStatefulService.cs │ ├── MyStatefulService.csproj │ ├── PackageRoot │ │ ├── Config │ │ │ └── Settings.xml │ │ └── ServiceManifest.xml │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ServiceEventSource.cs │ └── packages.config ├── ServiceFabric.BackupRestore.Demo.sln ├── ServiceFabric.BackupRestore.Demo │ ├── ApplicationPackageRoot │ │ └── ApplicationManifest.xml │ ├── ApplicationParameters │ │ ├── Cloud.xml │ │ ├── Local.1Node.xml │ │ └── Local.5Node.xml │ ├── PublishProfiles │ │ ├── Cloud.xml │ │ ├── Local.1Node.xml │ │ └── Local.5Node.xml │ ├── Scripts │ │ └── Deploy-FabricApplication.ps1 │ ├── ServiceFabric.BackupRestore.Demo.sfproj │ ├── app.config │ └── packages.config ├── ServiceFabric.BackupRestore.Web │ ├── .bowerrc │ ├── Controllers │ │ ├── HomeController.cs │ │ └── ServiceDiscoveryController.cs │ ├── Models │ │ ├── BackupEnabledServiceReference.cs │ │ └── ErrorViewModel.cs │ ├── PackageRoot │ │ ├── Config │ │ │ └── Settings.xml │ │ └── ServiceManifest.xml │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── ScaffoldingReadMe.txt │ ├── ServiceEventSource.cs │ ├── ServiceFabric.BackupRestore.Web.csproj │ ├── Startup.cs │ ├── Views │ │ ├── Home │ │ │ ├── About.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Index.cshtml │ │ ├── Service.cshtml │ │ ├── ServiceDiscovery │ │ │ └── View.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── Web.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bower.json │ ├── bundleconfig.json │ └── wwwroot │ │ ├── css │ │ ├── site.css │ │ └── site.min.css │ │ ├── favicon.ico │ │ ├── images │ │ ├── banner1.svg │ │ ├── banner2.svg │ │ ├── banner3.svg │ │ └── banner4.svg │ │ ├── js │ │ ├── site.js │ │ └── site.min.js │ │ └── lib │ │ ├── bootstrap │ │ ├── .bower.json │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ └── js │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.min.js │ │ │ └── npm.js │ │ ├── jquery-validation-unobtrusive │ │ ├── .bower.json │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ │ ├── jquery-validation │ │ ├── .bower.json │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ │ └── jquery │ │ ├── .bower.json │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map ├── ServiceFabricServicesBackup │ └── MyStatefulService │ │ ├── App.config │ │ ├── MyStatefulService.cs │ │ ├── MyStatefulService.csproj │ │ ├── PackageRoot │ │ ├── Config │ │ │ └── Settings.xml │ │ └── ServiceManifest.xml │ │ ├── Program.cs │ │ ├── Properties │ │ └── AssemblyInfo.cs │ │ ├── ServiceEventSource.cs │ │ └── packages.config └── TestConsole │ ├── App.config │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── TestConsole.csproj │ └── packages.config ├── src └── ServiceFabric.BackupRestore │ ├── BackupMetadata.cs │ ├── BackupRestoreActorService.cs │ ├── BackupRestoreService.cs │ ├── BackupRestoreServiceOperations.cs │ ├── BlobStore.cs │ ├── CentralBackupStore.cs │ ├── FileStore.cs │ ├── IBackupRestoreService.cs │ ├── IBackupRestoreServiceOperations.cs │ ├── ICentralBackupStore.cs │ ├── Properties │ ├── AssemblyInfo.cs │ └── PublishProfiles │ │ └── FolderProfile.pubxml │ └── ServiceFabric.BackupRestore.csproj └── test └── ServiceFabric.BackupRestore.Tests ├── BackupRestoreServiceInternalExtensionsTests.cs ├── BlobStoreTests.cs ├── FileStoreTests.cs ├── Properties └── AssemblyInfo.cs └── ServiceFabric.BackupRestore.Tests.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ServiceFabric.BackupRestore 2 | ServiceFabric.BackupRestore simplifies creating and restoring backups for Reliable Stateful Service replicas. Store your backups safely outside of the cluster by using / implementing ICentralBackupStore. 3 | Just implement an interface! 4 | 5 | ## Note 6 | Once the SF team delivers their backup / restore solution, this package will likely become obsolete. 7 | https://github.com/Azure/service-fabric-issues/issues/730 8 | 9 | ## Change log 10 | - 3.1.3 11 | - Upgraded nuget packages (SF 3.0.480) 12 | 13 | - 3.1.1 14 | - Upgraded nuget packages (SF 2.8.232) 15 | 16 | - 3.1.0 17 | - Upgraded nuget packages (SF 2.8.211) 18 | 19 | - 3.0.0 20 | - Added BlobStore that saves backup data in an Azure Storage Account 21 | - No longer needed to inherit Stateful Services, just implement one or two interfaces. (added sample code below) 22 | 23 | - 2.3.1 24 | - Upgraded nuget packages (SF 2.7.198) 25 | 26 | - 2.3.0 27 | - Upgraded nuget packages (SF 2.6.220) 28 | 29 | - 2.2.0 30 | - Merged PR by bitTobiasMeier to fix issue in Actor backups 31 | - Merged PR by ypom to fix issue with multiple full backups 32 | - Fixed more issues in restoring backups 33 | 34 | - 2.1.0 35 | - Upgraded nuget packages (SF 2.6.210) 36 | 37 | - 2.0.0 38 | - Upgraded sln to VS2017 39 | - Upgraded nuget packages (SF 2.6.204) 40 | 41 | - 1.2.0 42 | - Upgraded nuget packages (SF 2.5.216, JSON 10.0.1) 43 | 44 | - 1.1.0 45 | - Added support for DataLossMode when restoring backups. 46 | - Added support for all partition types. 47 | 48 | - 1.0.0 49 | - Added support for incremental backups. 50 | - Added support for ActorServices using `BackupRestoreActorService` 51 | 52 | - 0.9.2 First version. 53 | - This version has only 1 central store implementation: FileStore. 54 | - It works for Stateful Services only. 55 | - It takes full backups, and performs forced restores. 56 | - Make sure to store your backup files outside of your cluster. 57 | - Contributions are more than welcome! 58 | 59 | ## Demo 60 | Run the demo app on your local dev cluster, to see how it works. 61 | https://github.com/loekd/ServiceFabric.BackupRestore/tree/master/demo 62 | Change the code in https://github.com/loekd/ServiceFabric.BackupRestore/blob/master/demo/MyStatefulService/Program.cs use the Azure Blob based store, or the File System based store. 63 | 64 | ## Enable your Stateful Service for Backup & Restore: 65 | 66 | 1. Add the nuget package https://www.nuget.org/packages/ServiceFabric.BackupRestore/ 67 | 2. Have your Stateful Service implement ```IBackupRestoreServiceOperations``` 68 | Inject an instance of a type that implements `ICentralBackupStore`, for example `IBlobStore` or `IFileStore`. You can also implement your own types. 69 | To implement ```IBackupRestoreServiceOperations```, delegate most of the work to `BackupRestoreServiceOperations`. 70 | 71 | ``` csharp 72 | internal sealed class MyStatefulService : StatefulService, IBackupRestoreServiceOperations, IMyStatefulService 73 | { 74 | public MyStatefulService(StatefulServiceContext context, ICentralBackupStore centralBackupStore, Action logCallback) 75 | : base(context) 76 | { 77 | _centralBackupStore = centralBackupStore ?? throw new ArgumentNullException(nameof(centralBackupStore)); 78 | } 79 | 80 | //////NOT IN INTERFACE: 81 | 82 | /// 83 | protected sealed override Task OnDataLossAsync(RestoreContext restoreCtx, CancellationToken cancellationToken) 84 | { 85 | //after data loss, we'll restore a backup here: 86 | return BackupRestoreServiceOperations.OnDataLossAsync(this, restoreCtx, cancellationToken); 87 | } 88 | 89 | 90 | /////IN INTERFACE: 91 | 92 | /// 93 | ICentralBackupStore IBackupRestoreServiceOperations.CentralBackupStore => _centralBackupStore; 94 | 95 | /// 96 | Action IBackupRestoreServiceOperations.LogCallback => _logCallback; 97 | 98 | /// 99 | IStatefulServicePartition IBackupRestoreServiceOperations.Partition => Partition; 100 | 101 | /// 102 | Task IBackupRestoreServiceOperations.PostBackupCallbackAsync(BackupInfo backupInfo, CancellationToken cancellationToken) 103 | { 104 | return this.PostBackupCallbackAsync(backupInfo, cancellationToken); 105 | } 106 | } 107 | ``` 108 | 3. Change Program.Main to provide an implementation of ```ICentralBackupStore``` e.g. the `BlobStore` or `FileStore`: 109 | 110 | ``` csharp 111 | ServiceRuntime.RegisterServiceAsync("MyStatefulServiceType", context => 112 | { 113 | //Use the blob store, combined with an Azure Storage Account, or the Storage Emulator for testing. 114 | var centralBackupStore = new BlobStore("UseDevelopmentStorage=true", serviceName); 115 | //Or the file store: 116 | string serviceName = context.ServiceName.AbsoluteUri.Replace(":", string.Empty).Replace("/", "-"); 117 | string remoteFolderName = Path.Combine(@"E:\sfbackups", serviceName); 118 | //The E drive is a mapped network share to a File Server outside of the cluster here. 119 | //make sure the account running this service has R/W access to that location. 120 | var centralBackupStore = new FileStore(remoteFolderName); 121 | return new MyStatefulService(context, centralBackupStore, log => ServiceEventSource.Current.ServiceMessage(context, log)); 122 | }).GetAwaiter().GetResult(); 123 | ``` 124 | 125 | 4. Optionally, enable communication with your service, for instance using SF Remoting by implementing the interface `IBackupRestoreService`. 126 | Again, delegate the most of the work of the operations, to `BackupRestoreServiceOperations`. 127 | 128 | ``` csharp 129 | internal sealed class MyStatefulService : StatefulService, IBackupRestoreServiceOperations, IMyStatefulService, IBackupRestoreService 130 | { 131 | //////NOT IN INTERFACE: 132 | 133 | [..] 134 | protected override IEnumerable CreateServiceReplicaListeners() 135 | { 136 | //Enable interaction, to allow external callers to trigger backups and restores, by using Service Remoting through IBackupRestoreService 137 | yield return new ServiceReplicaListener(this.CreateServiceRemotingListener, BackupRestoreService.BackupRestoreServiceEndpointName); 138 | } 139 | 140 | 141 | 142 | //////IN INTERFACE: 143 | 144 | /// 145 | Task IBackupRestoreService.BeginCreateBackup(BackupOption backupOption) 146 | { 147 | return BackupRestoreServiceOperations.BeginCreateBackup(this, backupOption); 148 | } 149 | 150 | /// 151 | Task IBackupRestoreService.BeginRestoreBackup(BackupMetadata backupMetadata, DataLossMode dataLossMode) 152 | { 153 | return BackupRestoreServiceOperations.BeginRestoreBackup(this, backupMetadata, dataLossMode); 154 | } 155 | 156 | /// 157 | Task> IBackupRestoreService.ListBackups() 158 | { 159 | return BackupRestoreServiceOperations.ListBackups(this); 160 | } 161 | 162 | /// 163 | Task> IBackupRestoreService.ListAllBackups() 164 | { 165 | return BackupRestoreServiceOperations.ListAllBackups(this); 166 | } 167 | } 168 | ``` 169 | 170 | ``` csharp 171 | ``` 172 | 173 | ### Inheritance is optional 174 | 175 | You can also implement the required interfaces by inheriting from `ServiceFabric.BackupRestore.BackupRestoreService` or `ServiceFabric.BackupRestore.BackupRestoreActorService`. 176 | 177 | 178 | ## Optional calling application 179 | 180 | 1. Create an application that calls your Service to perform Backup & Restore operations 181 | 2. Add the nuget package to your calling application too: https://www.nuget.org/packages/ServiceFabric.BackupRestore/ 182 | 3. Add a reference to the Stateful Service project 183 | 4. Create a full backup asynchronously 184 | 185 | ``` csharp 186 | var proxy = ServiceProxy.Create(ServiceUri, servicePartitionKey); 187 | await proxy.BeginCreateBackup(BackupOption.Full); //use BackupOption.Incremental for incremental backup 188 | ``` 189 | 5. List all central backups 190 | 191 | ``` csharp 192 | var proxy = ServiceProxy.Create(ServiceUri, servicePartitionKey); 193 | var list = await proxy.ListAllBackups(); 194 | Console.WriteLine($"Backup Id\t\t\t\tOriginal partition"); 195 | Console.WriteLine(string.Join(Environment.NewLine, list.Select(data => $" {data.BackupId}\t{data.OriginalServicePartitionId}"))); 196 | ``` 197 | 6. Restore a backup asynchronously 198 | 199 | ``` csharp 200 | var proxy = ServiceProxy.Create(ServiceUri, servicePartitionKey); 201 | var backups = (await proxy.ListBackups()).ToList(); 202 | int index = 0; //or any other list item! 203 | await proxy.BeginRestoreBackup(backups[index]); 204 | ``` 205 | -------------------------------------------------------------------------------- /ServiceFabric.BackupRestore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26510.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E9088977-DF5A-49A6-ACF3-853484ADFDA7}" 7 | ProjectSection(SolutionItems) = preProject 8 | License.md = License.md 9 | README.md = README.md 10 | EndProjectSection 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceFabric.BackupRestore", "src\ServiceFabric.BackupRestore\ServiceFabric.BackupRestore.csproj", "{42BE1897-995D-4466-9E7C-7A3EC9035EF9}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceFabric.BackupRestore.Tests", "test\ServiceFabric.BackupRestore.Tests\ServiceFabric.BackupRestore.Tests.csproj", "{6CD12A6A-6136-4354-AC9B-4F0A6224DE8C}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|x64 = Debug|x64 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {42BE1897-995D-4466-9E7C-7A3EC9035EF9}.Debug|x64.ActiveCfg = Debug|Any CPU 23 | {42BE1897-995D-4466-9E7C-7A3EC9035EF9}.Debug|x64.Build.0 = Debug|Any CPU 24 | {42BE1897-995D-4466-9E7C-7A3EC9035EF9}.Release|x64.ActiveCfg = Release|Any CPU 25 | {42BE1897-995D-4466-9E7C-7A3EC9035EF9}.Release|x64.Build.0 = Release|Any CPU 26 | {6CD12A6A-6136-4354-AC9B-4F0A6224DE8C}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {6CD12A6A-6136-4354-AC9B-4F0A6224DE8C}.Debug|x64.Build.0 = Debug|Any CPU 28 | {6CD12A6A-6136-4354-AC9B-4F0A6224DE8C}.Release|x64.ActiveCfg = Release|Any CPU 29 | {6CD12A6A-6136-4354-AC9B-4F0A6224DE8C}.Release|x64.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /demo/MyStatefulService/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /demo/MyStatefulService/MyStatefulService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Fabric; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | using Microsoft.ServiceFabric.Data.Collections; 7 | using Microsoft.ServiceFabric.Services.Communication.Runtime; 8 | using ServiceFabric.BackupRestore; 9 | using Microsoft.ServiceFabric.Services.Remoting.Runtime; 10 | using Microsoft.ServiceFabric.Services.Runtime; 11 | using System.Threading; 12 | using Microsoft.ServiceFabric.Services.Remoting; 13 | using Microsoft.ServiceFabric.Services.Remoting.FabricTransport; 14 | using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime; 15 | 16 | [assembly: FabricTransportServiceRemotingProvider(RemotingListener = RemotingListener.V2Listener, RemotingClient = RemotingClient.V2Client)] 17 | 18 | namespace MyStatefulService 19 | { 20 | /// 21 | /// Inherits from 22 | /// 23 | internal sealed class MyStatefulService : StatefulService, IBackupRestoreService, IBackupRestoreServiceOperations, IMyStatefulService 24 | { 25 | private readonly ICentralBackupStore _centralBackupStore; 26 | private readonly Action _logCallback; 27 | 28 | /// 29 | /// Creates a new instance. 30 | /// 31 | /// 32 | /// 33 | /// 34 | public MyStatefulService(StatefulServiceContext context, ICentralBackupStore centralBackupStore, Action logCallback) 35 | : base(context) 36 | { 37 | _centralBackupStore = centralBackupStore ?? throw new ArgumentNullException(nameof(centralBackupStore)); 38 | _logCallback = logCallback; 39 | } 40 | 41 | /// 42 | /// Creates a new instance. 43 | /// 44 | /// 45 | /// 46 | /// 47 | /// 48 | public MyStatefulService(StatefulServiceContext context, IReliableStateManagerReplica2 reliableStateManagerReplica, ICentralBackupStore centralBackupStore, Action logCallback) 49 | : base(context, reliableStateManagerReplica) 50 | { 51 | _centralBackupStore = centralBackupStore ?? throw new ArgumentNullException(nameof(centralBackupStore)); 52 | _logCallback = logCallback; 53 | } 54 | 55 | /// 56 | /// Returns a Service Remoting Listener that can be used to perform backup and restore operations on this replica. 57 | /// 58 | /// A collection of listeners. 59 | protected override IEnumerable CreateServiceReplicaListeners() 60 | { 61 | //Enable interaction, to allow external callers to trigger backups and restores, by using Service Remoting through IBackupRestoreService 62 | //yield return new ServiceReplicaListener(this.CreateServiceRemotingListener, BackupRestoreService.BackupRestoreServiceEndpointName); 63 | //return this.CreateServiceRemotingReplicaListeners(); 64 | yield return new ServiceReplicaListener(context => new FabricTransportServiceRemotingListener(context, this), BackupRestoreService.BackupRestoreServiceEndpointName); 65 | 66 | } 67 | 68 | /// 69 | /// Call this to set some state. 70 | /// 71 | /// 72 | /// 73 | public async Task SetState(string value) 74 | { 75 | var myDictionary = await StateManager.GetOrAddAsync>("myDictionary"); 76 | 77 | using (var tx = StateManager.CreateTransaction()) 78 | { 79 | await myDictionary.AddOrUpdateAsync(tx, "MyState", value, (key, old) => value); 80 | await tx.CommitAsync(); 81 | } 82 | 83 | ServiceEventSource.Current.ServiceMessage(Context, $"MyStatefulService - Set state to '{value}'."); 84 | } 85 | 86 | /// 87 | /// Call this to get the state that was set by calling 88 | /// 89 | /// 90 | public async Task GetState() 91 | { 92 | var myDictionary = await StateManager.GetOrAddAsync>("myDictionary"); 93 | 94 | using (var tx = StateManager.CreateTransaction()) 95 | { 96 | var result = await myDictionary.TryGetValueAsync(tx, "MyState"); 97 | string state = result.HasValue ? result.Value : "<<>>"; 98 | ServiceEventSource.Current.ServiceMessage(Context, $"MyStatefulService - Get state '{state}'."); 99 | return state; 100 | } 101 | } 102 | 103 | /// 104 | protected sealed override Task OnDataLossAsync(RestoreContext restoreCtx, CancellationToken cancellationToken) 105 | { 106 | //after data loss, we'll restore a backup here: 107 | return BackupRestoreServiceOperations.OnDataLossAsync(this, restoreCtx, cancellationToken); 108 | } 109 | 110 | /// 111 | Task IBackupRestoreService.BeginCreateBackup(BackupOption backupOption) 112 | { 113 | return BackupRestoreServiceOperations.BeginCreateBackup(this, backupOption); 114 | } 115 | 116 | /// 117 | Task IBackupRestoreService.BeginRestoreBackup(BackupMetadata backupMetadata, DataLossMode dataLossMode) 118 | { 119 | return BackupRestoreServiceOperations.BeginRestoreBackup(this, backupMetadata, dataLossMode); 120 | 121 | } 122 | 123 | /// 124 | Task> IBackupRestoreService.ListBackups() 125 | { 126 | return BackupRestoreServiceOperations.ListBackups(this); 127 | 128 | } 129 | 130 | /// 131 | Task> IBackupRestoreService.ListAllBackups() 132 | { 133 | return BackupRestoreServiceOperations.ListAllBackups(this); 134 | } 135 | 136 | /// 137 | ICentralBackupStore IBackupRestoreServiceOperations.CentralBackupStore => _centralBackupStore; 138 | 139 | /// 140 | Action IBackupRestoreServiceOperations.LogCallback => _logCallback; 141 | 142 | /// 143 | IStatefulServicePartition IBackupRestoreServiceOperations.Partition => Partition; 144 | 145 | /// 146 | Task IBackupRestoreServiceOperations.PostBackupCallbackAsync(BackupInfo backupInfo, CancellationToken cancellationToken) 147 | { 148 | return this.PostBackupCallbackAsync(backupInfo, cancellationToken); 149 | } 150 | 151 | } 152 | 153 | public interface IMyStatefulService : IBackupRestoreService 154 | { 155 | /// 156 | /// Save state 157 | /// 158 | /// 159 | /// 160 | Task SetState(string value); 161 | 162 | /// 163 | /// Query state 164 | /// 165 | /// 166 | Task GetState(); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /demo/MyStatefulService/PackageRoot/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 |
7 | 8 |
9 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /demo/MyStatefulService/PackageRoot/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | MyStatefulService.exe 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /demo/MyStatefulService/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Fabric; 4 | using System.IO; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.ServiceFabric.Services.Runtime; 8 | using ServiceFabric.BackupRestore; 9 | using System.Fabric.Management.ServiceModel; 10 | using System.Text.RegularExpressions; 11 | 12 | namespace MyStatefulService 13 | { 14 | internal static class Program 15 | { 16 | /// 17 | /// This is the entry point of the service host process. 18 | /// 19 | private static void Main() 20 | { 21 | try 22 | { 23 | //Register the service with a FileStore. 24 | ServiceRuntime.RegisterServiceAsync("MyStatefulServiceType", 25 | context => 26 | { 27 | Regex regEx = new Regex(@"[^a-zA-Z0-9]"); 28 | string serviceName = regEx.Replace(context.ServiceName.AbsoluteUri, "-").Replace("--", "-").ToLowerInvariant(); 29 | 30 | //enable this line to use the file based central store 31 | //var centralBackupStore = CreateFileStore(serviceName); 32 | 33 | //enable this line to use the azure blob storage based central store 34 | var centralBackupStore = CreateBlobStore(serviceName); 35 | 36 | return new MyStatefulService(context, centralBackupStore, log => ServiceEventSource.Current.ServiceMessage(context, log)); 37 | 38 | }).GetAwaiter().GetResult(); 39 | 40 | ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(MyStatefulService).Name); 41 | 42 | // Prevents this host process from terminating so services keep running. 43 | Thread.Sleep(Timeout.Infinite); 44 | } 45 | catch (Exception e) 46 | { 47 | ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString()); 48 | throw; 49 | } 50 | } 51 | 52 | private static ICentralBackupStore CreateBlobStore(string serviceName) 53 | { 54 | #warning This blob store is configured to use the Azure Storage Emulator. Replace the connection string with an actual storage account connectionstring for production scenarios. 55 | //see: https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator 56 | var blobStore = new BlobStore("UseDevelopmentStorage=true", serviceName); 57 | return blobStore; 58 | } 59 | 60 | private static ICentralBackupStore CreateFileStore(string serviceName) 61 | { 62 | string remoteFolderName = Path.Combine(@"c:\temp", serviceName); 63 | #warning change this folder in your own project! 64 | //this should not point to C:\ in production, instead use a mapped network share that stores data outside the cluster. 65 | //make sure the account running this service has R/W access to the location. 66 | Directory.CreateDirectory(remoteFolderName); 67 | var centralBackupStore = new FileStore(remoteFolderName); 68 | return centralBackupStore; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /demo/MyStatefulService/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MyStatefulService")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MyStatefulService")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b5e5472d-c9de-4f38-97a3-cf5311501575")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /demo/MyStatefulService/ServiceEventSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Fabric; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.ServiceFabric.Services.Runtime; 9 | 10 | namespace MyStatefulService 11 | { 12 | [EventSource(Name = "MyCompany-ServiceFabric.BackupRestore.Demo-MyStatefulService")] 13 | internal sealed class ServiceEventSource : EventSource 14 | { 15 | public static readonly ServiceEventSource Current = new ServiceEventSource(); 16 | 17 | static ServiceEventSource() 18 | { 19 | // A workaround for the problem where ETW activities do not get tracked until Tasks infrastructure is initialized. 20 | // This problem will be fixed in .NET Framework 4.6.2. 21 | Task.Run(() => { }); 22 | } 23 | 24 | // Instance constructor is private to enforce singleton semantics 25 | private ServiceEventSource() : base() { } 26 | 27 | #region Keywords 28 | // Event keywords can be used to categorize events. 29 | // Each keyword is a bit flag. A single event can be associated with multiple keywords (via EventAttribute.Keywords property). 30 | // Keywords must be defined as a public class named 'Keywords' inside EventSource that uses them. 31 | public static class Keywords 32 | { 33 | public const EventKeywords Requests = (EventKeywords)0x1L; 34 | public const EventKeywords ServiceInitialization = (EventKeywords)0x2L; 35 | } 36 | #endregion 37 | 38 | #region Events 39 | // Define an instance method for each event you want to record and apply an [Event] attribute to it. 40 | // The method name is the name of the event. 41 | // Pass any parameters you want to record with the event (only primitive integer types, DateTime, Guid & string are allowed). 42 | // Each event method implementation should check whether the event source is enabled, and if it is, call WriteEvent() method to raise the event. 43 | // The number and types of arguments passed to every event method must exactly match what is passed to WriteEvent(). 44 | // Put [NonEvent] attribute on all methods that do not define an event. 45 | // For more information see https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.aspx 46 | 47 | [NonEvent] 48 | public void Message(string message, params object[] args) 49 | { 50 | if (this.IsEnabled()) 51 | { 52 | string finalMessage = string.Format(message, args); 53 | Message(finalMessage); 54 | } 55 | } 56 | 57 | private const int MessageEventId = 1; 58 | [Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")] 59 | public void Message(string message) 60 | { 61 | if (this.IsEnabled()) 62 | { 63 | WriteEvent(MessageEventId, message); 64 | } 65 | } 66 | 67 | [NonEvent] 68 | public void ServiceMessage(StatefulServiceContext serviceContext, string message, params object[] args) 69 | { 70 | if (this.IsEnabled()) 71 | { 72 | string finalMessage = string.Format(message, args); 73 | ServiceMessage( 74 | serviceContext.ServiceName.ToString(), 75 | serviceContext.ServiceTypeName, 76 | serviceContext.ReplicaId, 77 | serviceContext.PartitionId, 78 | serviceContext.CodePackageActivationContext.ApplicationName, 79 | serviceContext.CodePackageActivationContext.ApplicationTypeName, 80 | serviceContext.NodeContext.NodeName, 81 | finalMessage); 82 | } 83 | } 84 | 85 | // For very high-frequency events it might be advantageous to raise events using WriteEventCore API. 86 | // This results in more efficient parameter handling, but requires explicit allocation of EventData structure and unsafe code. 87 | // To enable this code path, define UNSAFE conditional compilation symbol and turn on unsafe code support in project properties. 88 | private const int ServiceMessageEventId = 2; 89 | [Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")] 90 | private 91 | #if UNSAFE 92 | unsafe 93 | #endif 94 | void ServiceMessage( 95 | string serviceName, 96 | string serviceTypeName, 97 | long replicaOrInstanceId, 98 | Guid partitionId, 99 | string applicationName, 100 | string applicationTypeName, 101 | string nodeName, 102 | string message) 103 | { 104 | #if !UNSAFE 105 | WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message); 106 | #else 107 | const int numArgs = 8; 108 | fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message) 109 | { 110 | EventData* eventData = stackalloc EventData[numArgs]; 111 | eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) }; 112 | eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) }; 113 | eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) }; 114 | eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) }; 115 | eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) }; 116 | eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) }; 117 | eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) }; 118 | eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) }; 119 | 120 | WriteEventCore(ServiceMessageEventId, numArgs, eventData); 121 | } 122 | #endif 123 | } 124 | 125 | private const int ServiceTypeRegisteredEventId = 3; 126 | [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)] 127 | public void ServiceTypeRegistered(int hostProcessId, string serviceType) 128 | { 129 | WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType); 130 | } 131 | 132 | private const int ServiceHostInitializationFailedEventId = 4; 133 | [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)] 134 | public void ServiceHostInitializationFailed(string exception) 135 | { 136 | WriteEvent(ServiceHostInitializationFailedEventId, exception); 137 | } 138 | 139 | // A pair of events sharing the same name prefix with a "Start"/"Stop" suffix implicitly marks boundaries of an event tracing activity. 140 | // These activities can be automatically picked up by debugging and profiling tools, which can compute their execution time, child activities, 141 | // and other statistics. 142 | private const int ServiceRequestStartEventId = 5; 143 | [Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)] 144 | public void ServiceRequestStart(string requestTypeName) 145 | { 146 | WriteEvent(ServiceRequestStartEventId, requestTypeName); 147 | } 148 | 149 | private const int ServiceRequestStopEventId = 6; 150 | [Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)] 151 | public void ServiceRequestStop(string requestTypeName, string exception = "") 152 | { 153 | WriteEvent(ServiceRequestStopEventId, requestTypeName, exception); 154 | } 155 | #endregion 156 | 157 | #region Private methods 158 | #if UNSAFE 159 | private int SizeInBytes(string s) 160 | { 161 | if (s == null) 162 | { 163 | return 0; 164 | } 165 | else 166 | { 167 | return (s.Length + 1) * sizeof(char); 168 | } 169 | } 170 | #endif 171 | #endregion 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /demo/MyStatefulService/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{A07B5EB6-E848-4116-A8D0-A826331D98C6}") = "ServiceFabric.BackupRestore.Demo", "ServiceFabric.BackupRestore.Demo\ServiceFabric.BackupRestore.Demo.sfproj", "{4AA5F477-97E9-4104-810E-DA751472A69A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyStatefulService", "MyStatefulService\MyStatefulService.csproj", "{B5E5472D-C9DE-4F38-97A3-CF5311501575}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "TestConsole\TestConsole.csproj", "{A3C516E7-E212-4705-8B58-783DB572E804}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Release|Any CPU = Release|Any CPU 17 | Release|x64 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Debug|Any CPU.ActiveCfg = Debug|x64 21 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Debug|x64.ActiveCfg = Debug|x64 22 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Debug|x64.Build.0 = Debug|x64 23 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Debug|x64.Deploy.0 = Debug|x64 24 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Release|Any CPU.ActiveCfg = Release|x64 25 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Release|x64.ActiveCfg = Release|x64 26 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Release|x64.Build.0 = Release|x64 27 | {4AA5F477-97E9-4104-810E-DA751472A69A}.Release|x64.Deploy.0 = Release|x64 28 | {B5E5472D-C9DE-4F38-97A3-CF5311501575}.Debug|Any CPU.ActiveCfg = Debug|x64 29 | {B5E5472D-C9DE-4F38-97A3-CF5311501575}.Debug|x64.ActiveCfg = Debug|x64 30 | {B5E5472D-C9DE-4F38-97A3-CF5311501575}.Debug|x64.Build.0 = Debug|x64 31 | {B5E5472D-C9DE-4F38-97A3-CF5311501575}.Release|Any CPU.ActiveCfg = Release|x64 32 | {B5E5472D-C9DE-4F38-97A3-CF5311501575}.Release|x64.ActiveCfg = Release|x64 33 | {B5E5472D-C9DE-4F38-97A3-CF5311501575}.Release|x64.Build.0 = Release|x64 34 | {A3C516E7-E212-4705-8B58-783DB572E804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {A3C516E7-E212-4705-8B58-783DB572E804}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {A3C516E7-E212-4705-8B58-783DB572E804}.Debug|x64.ActiveCfg = Debug|Any CPU 37 | {A3C516E7-E212-4705-8B58-783DB572E804}.Debug|x64.Build.0 = Debug|Any CPU 38 | {A3C516E7-E212-4705-8B58-783DB572E804}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {A3C516E7-E212-4705-8B58-783DB572E804}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {A3C516E7-E212-4705-8B58-783DB572E804}.Release|x64.ActiveCfg = Release|Any CPU 41 | {A3C516E7-E212-4705-8B58-783DB572E804}.Release|x64.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/ApplicationPackageRoot/ApplicationManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/ApplicationParameters/Cloud.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/ApplicationParameters/Local.1Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/ApplicationParameters/Local.5Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/PublishProfiles/Cloud.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/PublishProfiles/Local.1Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/PublishProfiles/Local.5Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/ServiceFabric.BackupRestore.Demo.sfproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 4aa5f477-97e9-4104-810e-da751472a69a 6 | 2.0 7 | 1.5 8 | 1.6.5 9 | 10 | 11 | 12 | Debug 13 | x64 14 | 15 | 16 | Release 17 | x64 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Service Fabric Tools\Microsoft.VisualStudio.Azure.Fabric.ApplicationProject.targets 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Demo/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "wwwroot/lib" 3 | } 4 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using ServiceFabric.BackupRestore.Web.Models; 8 | 9 | namespace ServiceFabric.BackupRestore.Web.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | 14 | public IActionResult Index() 15 | { 16 | return View(); 17 | } 18 | 19 | public IActionResult About() 20 | { 21 | ViewData["Message"] = "Your application description page."; 22 | 23 | return View(); 24 | } 25 | 26 | public IActionResult Contact() 27 | { 28 | ViewData["Message"] = "Your contact page."; 29 | 30 | return View(); 31 | } 32 | 33 | public IActionResult Error() 34 | { 35 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Controllers/ServiceDiscoveryController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Fabric; 4 | using System.Fabric.Query; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.ServiceFabric.Services.Client; 10 | using Newtonsoft.Json.Linq; 11 | using ServiceFabric.BackupRestore.Web.Models; 12 | 13 | namespace ServiceFabric.BackupRestore.Web.Controllers 14 | { 15 | [Route("[controller]")] 16 | public class ServiceDiscoveryController : Controller 17 | { 18 | private readonly ServicePartitionResolver _servicePartitionResolver; 19 | private readonly FabricClient _fabricClient; 20 | 21 | public ServiceDiscoveryController(FabricClient fabricClient) 22 | { 23 | _servicePartitionResolver = ServicePartitionResolver.GetDefault(); 24 | _fabricClient = fabricClient ?? new FabricClient(); 25 | } 26 | 27 | /// 28 | /// Queries all applications for backup/restore enabled services. 29 | /// 30 | /// 31 | [HttpGet] 32 | public async Task Get() 33 | { 34 | var backupEnabledServices = await QueryBackupRestoreServices(); 35 | return View("View", backupEnabledServices); 36 | } 37 | 38 | /// 39 | /// Begins the process of creating a backup. 40 | /// 41 | /// 42 | [HttpPost("/createbackup")] 43 | public async Task CreateBackup([FromBody]BackupEnabledServiceReference service) 44 | { 45 | await Task.Delay(1); 46 | return View("View", service); 47 | } 48 | 49 | /// 50 | /// Begins the process of restoring a backup. 51 | /// 52 | /// 53 | [HttpPost("/restorebackup")] 54 | public async Task RestoreBackup([FromBody]BackupEnabledServiceReference service) 55 | { 56 | await Task.Delay(1); 57 | return View("View", service); 58 | } 59 | 60 | /// 61 | /// Query filtered by application, service and partition, for backup/restore enabled services. 62 | /// 63 | /// Query specific application 64 | /// Query specific servicename, without application prefix. Optional, but requires application to be specified as well. 65 | /// Query specific partitionId, optional 66 | /// 67 | [HttpGet("{application}/{service?}/{partitionId?}")] 68 | public async Task Get(string application, string service, Guid? partitionId = null) 69 | { 70 | var applicationName = string.IsNullOrWhiteSpace(application) ? null : new Uri(new Uri("fabric:/"), application); 71 | var serviceName = applicationName != null && !string.IsNullOrWhiteSpace(service) ? new Uri(applicationName.AbsoluteUri + "/" + service.Trim('/','\\')) : null; 72 | 73 | var backupEnabledServices = await QueryBackupRestoreServices(applicationName, serviceName, partitionId); 74 | return Ok(backupEnabledServices); 75 | } 76 | 77 | /// 78 | /// Queries all applications for backup/restore enabled services. 79 | /// 80 | /// Query specific application 81 | /// Query specific service 82 | /// Query specific partition 83 | /// 84 | private async Task> QueryBackupRestoreServices(Uri application = null, Uri service = null, Guid? partitionId = null) 85 | { 86 | var backupEnabledServices = new List(); 87 | string token; 88 | do 89 | { 90 | var apps = await _fabricClient.QueryManager.GetApplicationListAsync(application).ConfigureAwait(true); 91 | foreach (var app in apps) 92 | { 93 | await QueryApplicationServices(backupEnabledServices, app, service, partitionId); 94 | } 95 | token = apps.ContinuationToken; 96 | } while (token != null); 97 | 98 | return backupEnabledServices; 99 | } 100 | 101 | private async Task QueryApplicationServices(List backupEnabledServices, Application app, Uri serviceName = null, Guid? partitionId = null) 102 | { 103 | string token; 104 | do 105 | { 106 | var services = await _fabricClient.QueryManager.GetServiceListAsync(app.ApplicationName, serviceName).ConfigureAwait(true); 107 | foreach (var service in services) 108 | { 109 | await QueryServicePartitions(backupEnabledServices, app, service, partitionId); 110 | } 111 | token = services.ContinuationToken; 112 | } while (token != null); 113 | } 114 | 115 | private async Task QueryServicePartitions(List backupEnabledServices, Application app, Service service, Guid? partitionId) 116 | { 117 | string token; 118 | do 119 | { 120 | var partitions = await _fabricClient.QueryManager.GetPartitionListAsync(service.ServiceName, partitionId).ConfigureAwait(true); 121 | foreach (var partition in partitions) 122 | { 123 | ServicePartitionKey key; 124 | switch (partition.PartitionInformation.Kind) 125 | { 126 | //only int64 partitions are supported at this time 127 | case ServicePartitionKind.Int64Range: 128 | var longKey = (Int64RangePartitionInformation)partition.PartitionInformation; 129 | key = new ServicePartitionKey(longKey.LowKey); 130 | break; 131 | default: 132 | continue; 133 | } 134 | 135 | var resolved = await _servicePartitionResolver.ResolveAsync(service.ServiceName, key, CancellationToken.None).ConfigureAwait(true); 136 | foreach (var endpoint in resolved.Endpoints.Where(e => !string.IsNullOrWhiteSpace(e.Address))) 137 | { 138 | QueryPartitionEndpoints(backupEnabledServices, app, service, partition, endpoint); 139 | } 140 | } 141 | token = partitions.ContinuationToken; 142 | } while (token != null); 143 | } 144 | 145 | private void QueryPartitionEndpoints(List backupEnabledServices, Application app, Service service, Partition partition, ResolvedServiceEndpoint endpoint) 146 | { 147 | var endpointJson = JObject.Parse(endpoint.Address); 148 | var serviceEndpoint = endpointJson["Endpoints"][BackupRestoreService.BackupRestoreServiceEndpointName]; 149 | if (serviceEndpoint != null) 150 | { 151 | string endpointAddress = serviceEndpoint.Value(); 152 | var serviceDescription = new BackupEnabledServiceReference 153 | { 154 | ApplicationName = app.ApplicationName, 155 | ServiceName = service.ServiceName, 156 | Int64RangePartitionGuid = partition.PartitionInformation.Id, 157 | Endpoint = new Uri(endpointAddress) 158 | }; 159 | backupEnabledServices.Add(serviceDescription); 160 | } 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Models/BackupEnabledServiceReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ServiceFabric.BackupRestore.Web.Models 4 | { 5 | public class BackupEnabledServiceReference 6 | { 7 | public Uri ApplicationName { get; set; } 8 | 9 | public Uri ServiceName { get; set; } 10 | 11 | public Guid Int64RangePartitionGuid { get; set; } 12 | 13 | public Uri Endpoint { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ServiceFabric.BackupRestore.Web.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/PackageRoot/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/PackageRoot/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ServiceFabric.BackupRestore.Web.exe 18 | CodePackage 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ServiceFabric.Services.Runtime; 2 | using System; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace ServiceFabric.BackupRestore.Web 10 | { 11 | internal static class Program 12 | { 13 | /// 14 | /// This is the entry point of the service host process. 15 | /// 16 | private static void Main() 17 | { 18 | try 19 | { 20 | // The ServiceManifest.XML file defines one or more service type names. 21 | // Registering a service maps a service type name to a .NET type. 22 | // When Service Fabric creates an instance of this service type, 23 | // an instance of the class is created in this host process. 24 | 25 | ServiceRuntime.RegisterServiceAsync("ServiceFabric.BackupRestore.WebType", 26 | context => new Web(context)).GetAwaiter().GetResult(); 27 | 28 | ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Web).Name); 29 | 30 | // Prevents this host process from terminating so services keeps running. 31 | Thread.Sleep(Timeout.Infinite); 32 | } 33 | catch (Exception e) 34 | { 35 | ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString()); 36 | RunStandAlone(); 37 | //throw; 38 | } 39 | } 40 | 41 | private static void RunStandAlone() 42 | { 43 | var host = new WebHostBuilder() 44 | .UseKestrel() 45 | .UseContentRoot(Directory.GetCurrentDirectory()) 46 | .UseIISIntegration() 47 | .UseStartup() 48 | .Build(); 49 | 50 | host.Run(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:4011/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "ServiceFabric.BackupRestore.Web": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:4012/" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 |  2 | ASP.NET MVC core dependencies have been added to the project. 3 | (These dependencies include packages required to enable scaffolding) 4 | 5 | However you may still need to do make changes to your project. 6 | 7 | 1. Suggested changes to Startup class: 8 | 1.1 Add a constructor: 9 | public IConfiguration Configuration { get; } 10 | 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 1.2 Add MVC services: 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | // Add framework services. 19 | services.AddMvc(); 20 | } 21 | 22 | 1.3 Configure web app to use use Configuration and use MVC routing: 23 | 24 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 25 | { 26 | if (env.IsDevelopment()) 27 | { 28 | app.UseDeveloperExceptionPage(); 29 | } 30 | else 31 | { 32 | app.UseExceptionHandler("/Home/Error"); 33 | } 34 | 35 | app.UseStaticFiles(); 36 | 37 | app.UseMvc(routes => 38 | { 39 | routes.MapRoute( 40 | name: "default", 41 | template: "{controller=Home}/{action=Index}/{id?}"); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/ServiceFabric.BackupRestore.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net461 5 | win7-x64 6 | True 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | bin\Release\net461\win7-x64\ServiceFabric.BackupRestore.Web.xml 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using System.Fabric; 2 | using System.IO; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.PlatformAbstractions; 8 | using Swashbuckle.AspNetCore.Swagger; 9 | 10 | namespace ServiceFabric.BackupRestore.Web 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddMvc(); 25 | services.AddSingleton(_ => new FabricClient()); 26 | 27 | // Add Swagger file gen 28 | // Register the Swagger generator, defining one or more Swagger documents 29 | services.AddSwaggerGen(c => 30 | { 31 | c.SwaggerDoc("v1", new Info { Title = "Dispatching API", Version = "v1" }); 32 | var basePath = PlatformServices.Default.Application.ApplicationBasePath; 33 | var xmlPath = Path.Combine(basePath, "ServiceFabric.BackupRestore.Web.xml"); 34 | if (File.Exists(xmlPath)) 35 | { 36 | c.IncludeXmlComments(xmlPath); 37 | } 38 | }); 39 | 40 | } 41 | 42 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 43 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 44 | { 45 | if (env.IsDevelopment()) 46 | { 47 | app.UseDeveloperExceptionPage(); 48 | app.UseBrowserLink(); 49 | } 50 | else 51 | { 52 | app.UseExceptionHandler("/Home/Error"); 53 | } 54 | 55 | app.UseStaticFiles(); 56 | 57 | app.UseMvc(routes => 58 | { 59 | routes.MapRoute( 60 | name: "default", 61 | template: "{controller=Home}/{action=Index}/{id?}"); 62 | }); 63 | 64 | // Enable middleware to serve generated Swagger as a JSON endpoint. 65 | app.UseSwagger(); 66 | 67 | // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint. 68 | app.UseSwaggerUI(c => 69 | { 70 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "Dispatching API v1"); 71 | }); 72 | 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About"; 3 | } 4 |

@ViewData["Title"]

5 |

@ViewData["Message"]

6 | 7 |

Use this area to provide additional information.

8 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Contact"; 3 | } 4 |

@ViewData["Title"]

5 |

@ViewData["Message"]

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
18 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 | 6 | @* 68 | 69 |
70 |
71 |

Application uses

72 |
    73 |
  • Sample pages using ASP.NET Core MVC
  • 74 |
  • Bower for managing client-side libraries
  • 75 |
  • Theming using Bootstrap
  • 76 |
77 |
78 | 89 | 101 |
102 |

Run & Deploy

103 | 108 |
109 |
*@ 110 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Service.cshtml: -------------------------------------------------------------------------------- 1 | @model BackupEnabledServiceReference 2 | @{ 3 | ViewData["Title"] = "View"; 4 | } 5 | @{ 6 | ViewBag.Title = "title"; 7 | Layout = "_Layout"; 8 | } 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 |
18 |
-------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/ServiceDiscovery/View.cshtml: -------------------------------------------------------------------------------- 1 | @model List 2 | @{ 3 | ViewData["Title"] = "View"; 4 | } 5 |

Services that support Backup Restore

6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @{ 21 | int i = 0; 22 | } 23 | @foreach (var serviceReference in Model) 24 | { 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | 36 |
#ApplicationServicePartitionEndpoint
@{ i++; }@serviceReference.ApplicationName@serviceReference.ServiceName@serviceReference.Int64RangePartitionGuid@serviceReference.Endpoint
37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 22 |

23 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - ServiceFabric.BackupRestore.Web 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 40 |
41 | @RenderBody() 42 |
43 |
44 |

© 2017 - ServiceFabric.BackupRestore.Web

45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 66 | 67 | 68 | 69 | @RenderSection("Scripts", required: false) 70 | 71 | 72 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using ServiceFabric.BackupRestore.Web 2 | @using ServiceFabric.BackupRestore.Web.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/Web.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Fabric; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.ServiceFabric.Services.Client; 11 | using Microsoft.ServiceFabric.Services.Communication.AspNetCore; 12 | using Microsoft.ServiceFabric.Services.Communication.Client; 13 | using Microsoft.ServiceFabric.Services.Communication.Runtime; 14 | using Microsoft.ServiceFabric.Services.Remoting.Client; 15 | using Microsoft.ServiceFabric.Services.Runtime; 16 | using MyStatefulService; 17 | 18 | namespace ServiceFabric.BackupRestore.Web 19 | { 20 | /// 21 | /// The FabricRuntime creates an instance of this class for each service type instance. 22 | /// 23 | internal sealed class Web : StatelessService 24 | { 25 | public Web(StatelessServiceContext context) 26 | : base(context) 27 | { } 28 | 29 | /// 30 | /// Optional override to create listeners (like tcp, http) for this service instance. 31 | /// 32 | /// The collection of listeners. 33 | protected override IEnumerable CreateServiceInstanceListeners() 34 | { 35 | return new ServiceInstanceListener[] 36 | { 37 | new ServiceInstanceListener(serviceContext => 38 | new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) => 39 | { 40 | ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}"); 41 | 42 | return new WebHostBuilder() 43 | .UseKestrel() 44 | .ConfigureServices( 45 | services => services 46 | .AddSingleton(serviceContext)) 47 | .UseContentRoot(Directory.GetCurrentDirectory()) 48 | .UseStartup() 49 | .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None) 50 | .UseUrls(url) 51 | .Build(); 52 | })) 53 | }; 54 | } 55 | 56 | protected override async Task RunAsync(CancellationToken cancellationToken) 57 | { 58 | await CreateProxyAsync(-1L); 59 | // return base.RunAsync(cancellationToken); 60 | } 61 | 62 | private static readonly Uri ServiceUri = new Uri("fabric:/ServiceFabric.BackupRestore.Demo/MyStatefulService"); 63 | 64 | 65 | private static async Task CreateProxyAsync(long partitionKey) 66 | { 67 | IMyStatefulService proxy = null; 68 | 69 | while (proxy == null) 70 | { 71 | try 72 | { 73 | var servicePartitionKey = new ServicePartitionKey(partitionKey); 74 | proxy = ServiceProxy.Create(ServiceUri, servicePartitionKey, TargetReplicaSelector.Default, BackupRestoreService.BackupRestoreServiceEndpointName); 75 | var result = await proxy.ListBackups().ConfigureAwait(false); 76 | if (result != null) 77 | { 78 | break; 79 | } 80 | 81 | } 82 | catch 83 | { 84 | proxy = null; 85 | Console.Write("."); 86 | await Task.Delay(200); 87 | } 88 | } 89 | return proxy; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp.net", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "3.3.7", 6 | "jquery": "2.2.0", 7 | "jquery-validation": "1.14.0", 8 | "jquery-validation-unobtrusive": "3.2.6" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loekd/ServiceFabric.BackupRestore/b48a7a4060b2578bfa5860c184cc2ef430b39223/demo/ServiceFabric.BackupRestore.Web/wwwroot/favicon.ico -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your JavaScript code. 2 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loekd/ServiceFabric.BackupRestore/b48a7a4060b2578bfa5860c184cc2ef430b39223/demo/ServiceFabric.BackupRestore.Web/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loekd/ServiceFabric.BackupRestore/b48a7a4060b2578bfa5860c184cc2ef430b39223/demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loekd/ServiceFabric.BackupRestore/b48a7a4060b2578bfa5860c184cc2ef430b39223/demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loekd/ServiceFabric.BackupRestore/b48a7a4060b2578bfa5860c184cc2ef430b39223/demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loekd/ServiceFabric.BackupRestore/b48a7a4060b2578bfa5860c184cc2ef430b39223/demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /demo/ServiceFabric.BackupRestore.Web/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/MyStatefulService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Fabric; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | using Microsoft.ServiceFabric.Data.Collections; 7 | using Microsoft.ServiceFabric.Services.Communication.Runtime; 8 | using ServiceFabric.BackupRestore; 9 | using Microsoft.ServiceFabric.Services.Remoting.Runtime; 10 | 11 | namespace MyStatefulService 12 | { 13 | /// 14 | /// Inherits from 15 | /// 16 | internal sealed class MyStatefulService : BackupRestoreService, IMyStatefulService 17 | { 18 | public MyStatefulService(StatefulServiceContext context, ICentralBackupStore centralBackupStore, Action logCallback) 19 | : base(context, centralBackupStore, logCallback) 20 | { 21 | } 22 | 23 | public MyStatefulService(StatefulServiceContext context, IReliableStateManagerReplica reliableStateManagerReplica, ICentralBackupStore centralBackupStore, Action logCallback) 24 | : base(context, reliableStateManagerReplica, centralBackupStore, logCallback) 25 | { 26 | } 27 | 28 | /// 29 | /// Returns a Service Remoting Listener that can be used to perform backup and restore operations on this replica. 30 | /// 31 | /// A collection of listeners. 32 | protected override IEnumerable CreateServiceReplicaListeners() 33 | { 34 | yield return new ServiceReplicaListener(this.CreateServiceRemotingListener, BackupRestoreServiceEndpointName); 35 | } 36 | 37 | public async Task SetState(string value) 38 | { 39 | var myDictionary = await StateManager.GetOrAddAsync>("myDictionary"); 40 | 41 | using (var tx = StateManager.CreateTransaction()) 42 | { 43 | await myDictionary.AddOrUpdateAsync(tx, "MyState", value, (key, old) => value); 44 | await tx.CommitAsync(); 45 | } 46 | 47 | ServiceEventSource.Current.ServiceMessage(Context, $"MyStatefulService - Set state to '{value}'."); 48 | } 49 | 50 | public async Task GetState() 51 | { 52 | var myDictionary = await StateManager.GetOrAddAsync>("myDictionary"); 53 | 54 | using (var tx = StateManager.CreateTransaction()) 55 | { 56 | var result = await myDictionary.TryGetValueAsync(tx, "MyState"); 57 | string state = result.HasValue ? result.Value : "<<>>"; 58 | ServiceEventSource.Current.ServiceMessage(Context, $"MyStatefulService - Get state '{state}'."); 59 | return state; 60 | } 61 | } 62 | } 63 | 64 | public interface IMyStatefulService : IBackupRestoreService 65 | { 66 | /// 67 | /// Save state 68 | /// 69 | /// 70 | /// 71 | Task SetState(string value); 72 | 73 | /// 74 | /// Query state 75 | /// 76 | /// 77 | Task GetState(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/MyStatefulService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | {B5E5472D-C9DE-4F38-97A3-CF5311501575} 8 | Exe 9 | Properties 10 | MyStatefulService 11 | MyStatefulService 12 | v4.6.2 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\x64\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | x64 26 | MinimumRecommendedRules.ruleset 27 | 28 | 29 | pdbonly 30 | true 31 | bin\x64\Release\ 32 | TRACE 33 | prompt 34 | x64 35 | MinimumRecommendedRules.ruleset 36 | 37 | 38 | $(AdditionalFileItemNames);None 39 | 40 | 41 | true 42 | 43 | 44 | 45 | ..\packages\Microsoft.ServiceFabric.Actors.2.5.216\lib\net45\Microsoft.ServiceFabric.Actors.dll 46 | True 47 | 48 | 49 | ..\packages\Microsoft.ServiceFabric.Data.2.5.216\lib\net45\Microsoft.ServiceFabric.Data.dll 50 | True 51 | 52 | 53 | ..\packages\Microsoft.ServiceFabric.Data.2.5.216\lib\net45\Microsoft.ServiceFabric.Data.Interfaces.dll 54 | True 55 | 56 | 57 | ..\packages\Microsoft.ServiceFabric.FabricTransport.Internal.2.5.216\lib\net45\Microsoft.ServiceFabric.FabricTransport.dll 58 | True 59 | 60 | 61 | ..\packages\Microsoft.ServiceFabric.5.5.216\lib\net45\Microsoft.ServiceFabric.Internal.dll 62 | True 63 | 64 | 65 | ..\packages\Microsoft.ServiceFabric.5.5.216\lib\net45\Microsoft.ServiceFabric.Internal.Strings.dll 66 | True 67 | 68 | 69 | ..\packages\Microsoft.ServiceFabric.Services.2.5.216\lib\net45\Microsoft.ServiceFabric.Services.dll 70 | True 71 | 72 | 73 | ..\packages\Microsoft.ServiceFabric.Services.Remoting.2.5.216\lib\net45\Microsoft.ServiceFabric.Services.Remoting.dll 74 | True 75 | 76 | 77 | ..\packages\Newtonsoft.Json.10.0.1\lib\net45\Newtonsoft.Json.dll 78 | True 79 | 80 | 81 | ..\packages\ServiceFabric.BackupRestore.1.2.0\lib\net451\ServiceFabric.BackupRestore.dll 82 | True 83 | 84 | 85 | 86 | 87 | ..\packages\Microsoft.ServiceFabric.5.5.216\lib\net45\System.Fabric.dll 88 | True 89 | 90 | 91 | ..\packages\Microsoft.ServiceFabric.5.5.216\lib\net45\System.Fabric.Management.ServiceModel.dll 92 | True 93 | 94 | 95 | ..\packages\Microsoft.ServiceFabric.5.5.216\lib\net45\System.Fabric.Management.ServiceModel.XmlSerializers.dll 96 | True 97 | 98 | 99 | ..\packages\Microsoft.ServiceFabric.5.5.216\lib\net45\System.Fabric.Strings.dll 100 | True 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 122 | 123 | 124 | 125 | 132 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/PackageRoot/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
    5 | 6 |
    7 | 8 |
    9 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/PackageRoot/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | MyStatefulService.exe 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.ServiceFabric.Services.Runtime; 7 | using ServiceFabric.BackupRestore; 8 | 9 | namespace MyStatefulService 10 | { 11 | internal static class Program 12 | { 13 | /// 14 | /// This is the entry point of the service host process. 15 | /// 16 | private static void Main() 17 | { 18 | try 19 | { 20 | //Register the service with a FileStore. 21 | ServiceRuntime.RegisterServiceAsync("MyStatefulServiceType", 22 | context => 23 | { 24 | string serviceName = context.ServiceName.AbsoluteUri.Replace(":", string.Empty).Replace("/", "-"); 25 | string remoteFolderName = Path.Combine(@"E:\sfbackups", serviceName); 26 | //make sure the account running this service has R/W access to the location. 27 | var centralBackupStore = new FileStore(remoteFolderName); 28 | 29 | return new MyStatefulService(context, centralBackupStore, log => ServiceEventSource.Current.ServiceMessage(context, log)); 30 | 31 | }).GetAwaiter().GetResult(); 32 | 33 | ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(MyStatefulService).Name); 34 | 35 | // Prevents this host process from terminating so services keep running. 36 | Thread.Sleep(Timeout.Infinite); 37 | } 38 | catch (Exception e) 39 | { 40 | ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString()); 41 | throw; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MyStatefulService")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MyStatefulService")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b5e5472d-c9de-4f38-97a3-cf5311501575")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/ServiceEventSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Fabric; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.ServiceFabric.Services.Runtime; 9 | 10 | namespace MyStatefulService 11 | { 12 | [EventSource(Name = "MyCompany-ServiceFabric.BackupRestore.Demo-MyStatefulService")] 13 | internal sealed class ServiceEventSource : EventSource 14 | { 15 | public static readonly ServiceEventSource Current = new ServiceEventSource(); 16 | 17 | static ServiceEventSource() 18 | { 19 | // A workaround for the problem where ETW activities do not get tracked until Tasks infrastructure is initialized. 20 | // This problem will be fixed in .NET Framework 4.6.2. 21 | Task.Run(() => { }); 22 | } 23 | 24 | // Instance constructor is private to enforce singleton semantics 25 | private ServiceEventSource() : base() { } 26 | 27 | #region Keywords 28 | // Event keywords can be used to categorize events. 29 | // Each keyword is a bit flag. A single event can be associated with multiple keywords (via EventAttribute.Keywords property). 30 | // Keywords must be defined as a public class named 'Keywords' inside EventSource that uses them. 31 | public static class Keywords 32 | { 33 | public const EventKeywords Requests = (EventKeywords)0x1L; 34 | public const EventKeywords ServiceInitialization = (EventKeywords)0x2L; 35 | } 36 | #endregion 37 | 38 | #region Events 39 | // Define an instance method for each event you want to record and apply an [Event] attribute to it. 40 | // The method name is the name of the event. 41 | // Pass any parameters you want to record with the event (only primitive integer types, DateTime, Guid & string are allowed). 42 | // Each event method implementation should check whether the event source is enabled, and if it is, call WriteEvent() method to raise the event. 43 | // The number and types of arguments passed to every event method must exactly match what is passed to WriteEvent(). 44 | // Put [NonEvent] attribute on all methods that do not define an event. 45 | // For more information see https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.aspx 46 | 47 | [NonEvent] 48 | public void Message(string message, params object[] args) 49 | { 50 | if (this.IsEnabled()) 51 | { 52 | string finalMessage = string.Format(message, args); 53 | Message(finalMessage); 54 | } 55 | } 56 | 57 | private const int MessageEventId = 1; 58 | [Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")] 59 | public void Message(string message) 60 | { 61 | if (this.IsEnabled()) 62 | { 63 | WriteEvent(MessageEventId, message); 64 | } 65 | } 66 | 67 | [NonEvent] 68 | public void ServiceMessage(StatefulServiceContext serviceContext, string message, params object[] args) 69 | { 70 | if (this.IsEnabled()) 71 | { 72 | string finalMessage = string.Format(message, args); 73 | ServiceMessage( 74 | serviceContext.ServiceName.ToString(), 75 | serviceContext.ServiceTypeName, 76 | serviceContext.ReplicaId, 77 | serviceContext.PartitionId, 78 | serviceContext.CodePackageActivationContext.ApplicationName, 79 | serviceContext.CodePackageActivationContext.ApplicationTypeName, 80 | serviceContext.NodeContext.NodeName, 81 | finalMessage); 82 | } 83 | } 84 | 85 | // For very high-frequency events it might be advantageous to raise events using WriteEventCore API. 86 | // This results in more efficient parameter handling, but requires explicit allocation of EventData structure and unsafe code. 87 | // To enable this code path, define UNSAFE conditional compilation symbol and turn on unsafe code support in project properties. 88 | private const int ServiceMessageEventId = 2; 89 | [Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")] 90 | private 91 | #if UNSAFE 92 | unsafe 93 | #endif 94 | void ServiceMessage( 95 | string serviceName, 96 | string serviceTypeName, 97 | long replicaOrInstanceId, 98 | Guid partitionId, 99 | string applicationName, 100 | string applicationTypeName, 101 | string nodeName, 102 | string message) 103 | { 104 | #if !UNSAFE 105 | WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message); 106 | #else 107 | const int numArgs = 8; 108 | fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message) 109 | { 110 | EventData* eventData = stackalloc EventData[numArgs]; 111 | eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) }; 112 | eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) }; 113 | eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) }; 114 | eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) }; 115 | eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) }; 116 | eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) }; 117 | eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) }; 118 | eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) }; 119 | 120 | WriteEventCore(ServiceMessageEventId, numArgs, eventData); 121 | } 122 | #endif 123 | } 124 | 125 | private const int ServiceTypeRegisteredEventId = 3; 126 | [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)] 127 | public void ServiceTypeRegistered(int hostProcessId, string serviceType) 128 | { 129 | WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType); 130 | } 131 | 132 | private const int ServiceHostInitializationFailedEventId = 4; 133 | [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)] 134 | public void ServiceHostInitializationFailed(string exception) 135 | { 136 | WriteEvent(ServiceHostInitializationFailedEventId, exception); 137 | } 138 | 139 | // A pair of events sharing the same name prefix with a "Start"/"Stop" suffix implicitly marks boundaries of an event tracing activity. 140 | // These activities can be automatically picked up by debugging and profiling tools, which can compute their execution time, child activities, 141 | // and other statistics. 142 | private const int ServiceRequestStartEventId = 5; 143 | [Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)] 144 | public void ServiceRequestStart(string requestTypeName) 145 | { 146 | WriteEvent(ServiceRequestStartEventId, requestTypeName); 147 | } 148 | 149 | private const int ServiceRequestStopEventId = 6; 150 | [Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)] 151 | public void ServiceRequestStop(string requestTypeName, string exception = "") 152 | { 153 | WriteEvent(ServiceRequestStopEventId, requestTypeName, exception); 154 | } 155 | #endregion 156 | 157 | #region Private methods 158 | #if UNSAFE 159 | private int SizeInBytes(string s) 160 | { 161 | if (s == null) 162 | { 163 | return 0; 164 | } 165 | else 166 | { 167 | return (s.Length + 1) * sizeof(char); 168 | } 169 | } 170 | #endif 171 | #endregion 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /demo/ServiceFabricServicesBackup/MyStatefulService/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/TestConsole/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /demo/TestConsole/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Fabric; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.ServiceFabric.Data; 7 | using Microsoft.ServiceFabric.Services.Client; 8 | using Microsoft.ServiceFabric.Services.Communication.Client; 9 | using Microsoft.ServiceFabric.Services.Remoting.Client; 10 | using MyStatefulService; 11 | using ServiceFabric.BackupRestore; 12 | 13 | namespace TestConsole 14 | { 15 | internal class Program 16 | { 17 | private static readonly Uri ServiceUri; 18 | 19 | static Program() 20 | { 21 | ServiceUri = new Uri("fabric:/ServiceFabric.BackupRestore.Demo/MyStatefulService"); 22 | } 23 | 24 | private static void Main() 25 | { 26 | Task.WaitAll(Task.Run(async () => 27 | { 28 | try 29 | { 30 | await MainAsync(); 31 | } 32 | catch (Exception ex) 33 | { 34 | Console.WriteLine(ex); 35 | Console.WriteLine("There was an error. Press any key to exit."); 36 | Console.ReadKey(true); 37 | } 38 | })); 39 | } 40 | 41 | private static async Task MainAsync() 42 | { 43 | Console.WriteLine("Waiting for services...."); 44 | 45 | var proxyPartitionOne = await CreateProxyAsync(-1L); 46 | 47 | var proxyPartitionTwo = await CreateProxyAsync(1L); 48 | var proxy = proxyPartitionOne; 49 | 50 | Console.WriteLine("Waited for services.."); 51 | 52 | 53 | while (true) 54 | { 55 | Console.WriteLine($"Press any key to continue"); 56 | Console.ReadKey(true); 57 | Console.Clear(); 58 | 59 | Console.WriteLine("Press 0 to select target partition"); 60 | Console.WriteLine("Press 1 to get state"); 61 | Console.WriteLine("Press 2 to set state"); 62 | Console.WriteLine("Press 3 to create a backup"); 63 | Console.WriteLine("Press 4 to restore a backup"); 64 | Console.WriteLine("Press 5 to list all central backups"); 65 | Console.WriteLine("Press 6 to list the current Service Partition Ids"); 66 | Console.WriteLine("Press 7 to invoke full dataloss on one of the current Service's Partitions"); 67 | Console.WriteLine("Other key to exit"); 68 | 69 | var key = Console.ReadKey(true); 70 | string input; 71 | 72 | switch (key.Key) 73 | { 74 | case ConsoleKey.D0: 75 | Console.WriteLine("Type 1 for partition one, or 2 for partition two"); 76 | key = Console.ReadKey(true); 77 | if (ConsoleKey.D2 == key.Key) 78 | { 79 | proxy = proxyPartitionTwo; 80 | Console.WriteLine("Using partition two."); 81 | } 82 | else 83 | { 84 | proxy = proxyPartitionOne; 85 | Console.WriteLine("Using partition one."); 86 | } 87 | break; 88 | 89 | case ConsoleKey.D1: 90 | string state = await proxy.GetState(); 91 | Console.WriteLine($"State: '{state}'"); 92 | break; 93 | case ConsoleKey.D2: 94 | Console.WriteLine("Enter string to store as state:"); 95 | input = Console.ReadLine(); 96 | await proxy.SetState(input ?? ""); 97 | Console.WriteLine($"State saved: '{input}'"); 98 | break; 99 | case ConsoleKey.D3: 100 | Console.WriteLine("Type 1 for full backup or 2 for incremental backup (incremental requires full backup to exist)"); 101 | key = Console.ReadKey(true); 102 | if (ConsoleKey.D1 == key.Key) 103 | { 104 | Console.WriteLine("Creating a full backup asynchronously..."); 105 | await proxy.BeginCreateBackup(BackupOption.Full); 106 | } 107 | else 108 | { 109 | Console.WriteLine("Creating an incremental backup asynchronously..."); 110 | await proxy.BeginCreateBackup(BackupOption.Incremental); 111 | } 112 | 113 | break; 114 | case ConsoleKey.D4: 115 | Console.WriteLine($"Starting the restore of a backup"); 116 | Console.WriteLine($"Enter central backup id (guid):"); 117 | input = Console.ReadLine(); 118 | 119 | var backups = (await proxy.ListAllBackups()).ToList(); 120 | Guid index; 121 | if (Guid.TryParse(input, out index)) 122 | { 123 | DataLossMode lossMode = DataLossMode.FullDataLoss; 124 | Console.WriteLine("Type 1 for full data loss or 2 for partial data loss."); 125 | 126 | key = Console.ReadKey(true); 127 | if (ConsoleKey.D1 == key.Key) 128 | { 129 | Console.WriteLine("Restoring backup with full data loss asynchronously..."); 130 | } 131 | else 132 | { 133 | Console.WriteLine("Restoring backup with partial data loss asynchronously..."); 134 | lossMode = DataLossMode.PartialDataLoss; 135 | } 136 | 137 | await proxy.BeginRestoreBackup(backups.Single(b => b.BackupId == index), lossMode); 138 | Console.WriteLine($"Restore is active. This will take some time. Check progress in SF explorer."); 139 | } 140 | 141 | break; 142 | case ConsoleKey.D5: 143 | Console.WriteLine($"List all central backups"); 144 | var list = await proxy.ListAllBackups(); 145 | Console.WriteLine($"Original partition\t\t\tBackup Id\t\t\t\tBackup Type\tTimestamp UTC"); 146 | Console.WriteLine(string.Join(Environment.NewLine, list.Select(data => $"{data.OriginalServicePartitionId}\t{data.BackupId}\t{data.BackupOption}\t\t{data.TimeStampUtc}"))); 147 | break; 148 | 149 | case ConsoleKey.D6: 150 | var resolver = ServicePartitionResolver.GetDefault(); 151 | var resolved = await resolver.ResolveAsync(ServiceUri, new ServicePartitionKey(-1L), CancellationToken.None); 152 | Console.WriteLine($"Partition key -1L resolves to partition {resolved.Info.Id}"); 153 | resolved = await resolver.ResolveAsync(ServiceUri, new ServicePartitionKey(1L), CancellationToken.None); 154 | Console.WriteLine($"Partition key 1L resolves to partition {resolved.Info.Id}"); 155 | 156 | if (proxy == proxyPartitionOne) 157 | { 158 | Console.WriteLine("Using partition one (-1L)"); 159 | } 160 | else 161 | { 162 | Console.WriteLine("Using partition two (1L)"); 163 | } 164 | break; 165 | case ConsoleKey.D7: 166 | Console.WriteLine("Enter partitionID"); 167 | string partitionString = Console.ReadLine(); 168 | if (Guid.TryParse(partitionString, out Guid partitionID)) 169 | { 170 | var partitionSelector = PartitionSelector.PartitionIdOf(ServiceUri, partitionID); 171 | await new FabricClient(FabricClientRole.Admin).TestManager.StartPartitionDataLossAsync(Guid.NewGuid(), partitionSelector, DataLossMode.FullDataLoss); 172 | } 173 | break; 174 | default: 175 | return; 176 | } 177 | } 178 | } 179 | 180 | private static async Task CreateProxyAsync(long partitionKey) 181 | { 182 | Console.WriteLine($"Creating Proxy: CreateProxyAsync({partitionKey})"); 183 | 184 | IMyStatefulService proxy = null; 185 | 186 | while (proxy == null) 187 | { 188 | try 189 | { 190 | var servicePartitionKey = new ServicePartitionKey(partitionKey); 191 | proxy = ServiceProxy.Create(ServiceUri, servicePartitionKey, TargetReplicaSelector.Default, BackupRestoreService.BackupRestoreServiceEndpointName); 192 | 193 | Console.Write("calling proxy.ListBackups"); 194 | 195 | var result = await proxy.ListBackups(); 196 | if (result != null) 197 | { 198 | Console.WriteLine("succeeded"); 199 | break; 200 | } 201 | 202 | } 203 | catch(Exception ex) 204 | { 205 | Console.Write(ex.ToString()); 206 | proxy = null; 207 | Console.Write("."); 208 | await Task.Delay(200); 209 | } 210 | } 211 | return proxy; 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /demo/TestConsole/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestConsole")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestConsole")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a3c516e7-e212-4705-8b58-783db572e804")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /demo/TestConsole/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/BackupMetadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using Microsoft.ServiceFabric.Data; 4 | using Newtonsoft.Json; 5 | 6 | namespace ServiceFabric.BackupRestore 7 | { 8 | /// 9 | /// Metadata for ServiceFabric.BackupRestore backup. 10 | /// 11 | [DataContract] 12 | public class BackupMetadata 13 | { 14 | /// 15 | /// Unique identifier for a backup. 16 | /// 17 | [DataMember] 18 | public readonly Guid BackupId; 19 | 20 | /// 21 | /// Indicates the Stateful Service Parition Guid that the backup was created for. 22 | /// 23 | [DataMember] 24 | public readonly Guid OriginalServicePartitionId; 25 | 26 | /// 27 | /// Indicates when the backup was created. 28 | /// 29 | [DataMember] 30 | public readonly DateTime TimeStampUtc; 31 | 32 | /// 33 | /// Indicates the type of backup. 34 | /// 35 | [DataMember] 36 | public readonly BackupOption BackupOption; 37 | 38 | //maybe add file hashes? 39 | 40 | /// 41 | /// Creates a new instance using the provided arguments. 42 | /// 43 | /// Indicates the Stateful Service Parition Guid that the backup was created for. 44 | /// Indicates when the backup was created. 45 | /// Indicates the type of backup. 46 | public BackupMetadata(Guid originalServicePartitionId, DateTime timeStampUtc, BackupOption backupOption) 47 | : this(originalServicePartitionId, timeStampUtc, backupOption, Guid.NewGuid()) 48 | { 49 | } 50 | 51 | /// 52 | /// Creates a new instance using the provided arguments. 53 | /// 54 | /// Indicates the Stateful Service Parition Guid that the backup was created for. 55 | /// Indicates when the backup was created. 56 | /// Indicates the type of backup. 57 | /// Unique identifier for a backup. 58 | [JsonConstructor] 59 | public BackupMetadata(Guid originalServicePartitionId, DateTime timeStampUtc, BackupOption backupOption, Guid backupId) 60 | { 61 | BackupId = backupId; 62 | OriginalServicePartitionId = originalServicePartitionId; 63 | TimeStampUtc = timeStampUtc; 64 | BackupOption = backupOption; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/BackupRestoreActorService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Fabric; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.ServiceFabric.Actors; 7 | using Microsoft.ServiceFabric.Actors.Runtime; 8 | using Microsoft.ServiceFabric.Data; 9 | 10 | namespace ServiceFabric.BackupRestore 11 | { 12 | public abstract class BackupRestoreActorService : ActorService, IBackupRestoreService, IBackupRestoreServiceOperations 13 | { 14 | private readonly ICentralBackupStore _centralBackupStore; 15 | private readonly Action _logCallback; 16 | 17 | /// 18 | /// Endpoint at which this service may communicate through SF remoting and 19 | /// 20 | public const string BackupRestoreServiceEndpointName = "BackupRestoreActorServiceEndPoint"; 21 | 22 | /// 23 | /// Creates a new instance, using the provided arguments. 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | protected BackupRestoreActorService(StatefulServiceContext context, 34 | ActorTypeInformation actorTypeInfo, 35 | ICentralBackupStore centralBackupStore, 36 | Action logCallback, Func actorFactory = null, 37 | Func stateManagerFactory = null, 38 | IActorStateProvider stateProvider = null, 39 | ActorServiceSettings settings = null) 40 | : base(context, actorTypeInfo, actorFactory, stateManagerFactory, stateProvider, settings) 41 | { 42 | _centralBackupStore = centralBackupStore ?? throw new ArgumentNullException(nameof(centralBackupStore)); 43 | _logCallback = logCallback; 44 | } 45 | 46 | /// 47 | public Task BeginCreateBackup(BackupOption backupOption) 48 | { 49 | return BackupRestoreServiceOperations.BeginCreateBackup(this, backupOption); 50 | } 51 | 52 | /// 53 | public Task BeginRestoreBackup(BackupMetadata backupMetadata, DataLossMode dataLossMode) 54 | { 55 | return BackupRestoreServiceOperations.BeginRestoreBackup(this, backupMetadata, dataLossMode); 56 | } 57 | 58 | /// 59 | public Task> ListBackups() 60 | { 61 | return BackupRestoreServiceOperations.ListBackups(this); 62 | } 63 | 64 | /// 65 | public Task> ListAllBackups() 66 | { 67 | return BackupRestoreServiceOperations.ListAllBackups(this); 68 | } 69 | 70 | /// 71 | protected override Task OnDataLossAsync(RestoreContext restoreCtx, CancellationToken cancellationToken) 72 | { 73 | return BackupRestoreServiceOperations.OnDataLossAsync(this, restoreCtx, cancellationToken); 74 | } 75 | 76 | 77 | /// 78 | ICentralBackupStore IBackupRestoreServiceOperations.CentralBackupStore => _centralBackupStore; 79 | 80 | /// 81 | Action IBackupRestoreServiceOperations.LogCallback => _logCallback; 82 | 83 | /// 84 | IStatefulServicePartition IBackupRestoreServiceOperations.Partition => Partition; 85 | 86 | /// 87 | Task IBackupRestoreServiceOperations.PostBackupCallbackAsync(BackupInfo backupInfo, CancellationToken cancellationToken) 88 | { 89 | return this.PostBackupCallbackAsync(backupInfo, cancellationToken); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/BackupRestoreService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Fabric; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Microsoft.ServiceFabric.Data; 7 | using Microsoft.ServiceFabric.Services.Runtime; 8 | 9 | namespace ServiceFabric.BackupRestore 10 | { 11 | /// 12 | /// Base class of that enables simple backups and restore of State. 13 | /// Use SF remoting to talk to the Listenter named . 14 | /// Call to start a backup creation asynchronously. 15 | /// Call to start a restore operation asynchronously. 16 | /// 17 | public abstract class BackupRestoreService : StatefulService, IBackupRestoreService, IBackupRestoreServiceOperations 18 | { 19 | private readonly ICentralBackupStore _centralBackupStore; 20 | private readonly Action _logCallback; 21 | 22 | /// 23 | /// Endpoint at which this service may communicate through SF remoting and 24 | /// 25 | public const string BackupRestoreServiceEndpointName = "BackupRestoreServiceEndPoint"; 26 | 27 | /// 28 | /// Creates a new instance, using the provided arguments. 29 | /// 30 | /// 31 | /// 32 | /// 33 | protected BackupRestoreService(StatefulServiceContext context, ICentralBackupStore centralBackupStore, Action logCallback) 34 | : base(context) 35 | { 36 | _centralBackupStore = centralBackupStore ?? throw new ArgumentNullException(nameof(centralBackupStore)); 37 | _logCallback = logCallback; 38 | } 39 | 40 | /// 41 | /// Creates a new instance, using the provided arguments. 42 | /// 43 | /// 44 | /// 45 | /// 46 | /// 47 | protected BackupRestoreService(StatefulServiceContext context, IReliableStateManagerReplica2 reliableStateManagerReplica, ICentralBackupStore centralBackupStore, Action logCallback) 48 | : base(context, reliableStateManagerReplica) 49 | { 50 | _centralBackupStore = centralBackupStore ?? throw new ArgumentNullException(nameof(centralBackupStore)); 51 | _logCallback = logCallback; 52 | } 53 | 54 | 55 | /// 56 | public Task BeginCreateBackup(BackupOption backupOption) 57 | { 58 | return BackupRestoreServiceOperations.BeginCreateBackup(this, backupOption); 59 | } 60 | 61 | /// 62 | public Task BeginRestoreBackup(BackupMetadata backupMetadata, DataLossMode dataLossMode) 63 | { 64 | return BackupRestoreServiceOperations.BeginRestoreBackup(this, backupMetadata, dataLossMode); 65 | } 66 | 67 | /// 68 | public Task> ListBackups() 69 | { 70 | return BackupRestoreServiceOperations.ListBackups(this); 71 | } 72 | 73 | /// 74 | public Task> ListAllBackups() 75 | { 76 | return BackupRestoreServiceOperations.ListAllBackups(this); 77 | } 78 | 79 | /// 80 | protected override Task OnDataLossAsync(RestoreContext restoreCtx, CancellationToken cancellationToken) 81 | { 82 | return BackupRestoreServiceOperations.OnDataLossAsync(this, restoreCtx, cancellationToken); 83 | } 84 | 85 | /// 86 | ICentralBackupStore IBackupRestoreServiceOperations.CentralBackupStore => _centralBackupStore; 87 | 88 | /// 89 | Action IBackupRestoreServiceOperations.LogCallback => _logCallback; 90 | 91 | /// 92 | IStatefulServicePartition IBackupRestoreServiceOperations.Partition => Partition; 93 | 94 | /// 95 | Task IBackupRestoreServiceOperations.PostBackupCallbackAsync(BackupInfo backupInfo, CancellationToken cancellationToken) 96 | { 97 | return this.PostBackupCallbackAsync(backupInfo, cancellationToken); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/CentralBackupStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.ServiceFabric.Data; 3 | using Microsoft.WindowsAzure.Storage; 4 | 5 | namespace ServiceFabric.BackupRestore 6 | { 7 | /// 8 | /// Metadata specifically for . 9 | /// 10 | internal class FileBackupMetadata : BackupMetadata 11 | { 12 | /// 13 | /// Specifies the relative remote folder where a backup is stored. 14 | /// 15 | public string RelativeFolder { get; set; } 16 | 17 | 18 | public FileBackupMetadata(string relativeFolder, Guid originalServicePartitionId, DateTime timeStampUtc, BackupOption backupOption) 19 | : base(originalServicePartitionId, timeStampUtc, backupOption) 20 | { 21 | if (string.IsNullOrWhiteSpace(relativeFolder)) 22 | throw new ArgumentException("Value cannot be null or whitespace.", nameof(relativeFolder)); 23 | RelativeFolder = relativeFolder; 24 | } 25 | 26 | public FileBackupMetadata(string relativeFolder, Guid originalServicePartitionId, DateTime timeStampUtc, BackupOption backupOption, Guid backupId) 27 | : base(originalServicePartitionId, timeStampUtc, backupOption, backupId) 28 | { 29 | if (string.IsNullOrWhiteSpace(relativeFolder)) 30 | throw new ArgumentException("Value cannot be null or whitespace.", nameof(relativeFolder)); 31 | RelativeFolder = relativeFolder; 32 | } 33 | } 34 | 35 | /// 36 | /// Metadata specifically for . 37 | /// 38 | internal class BlobBackupMetadata : BackupMetadata 39 | { 40 | public BlobStorageUri BlockBlobUri { get; set; } 41 | 42 | public BlobBackupMetadata(BlobStorageUri blockBlobUri, Guid originalServicePartitionId, DateTime timeStampUtc, BackupOption backupOption) 43 | : base(originalServicePartitionId, timeStampUtc, backupOption) 44 | { 45 | BlockBlobUri = blockBlobUri ?? throw new ArgumentNullException(nameof(blockBlobUri)); 46 | } 47 | 48 | public BlobBackupMetadata(BlobStorageUri blockBlobUri, Guid originalServicePartitionId, DateTime timeStampUtc, BackupOption backupOption, Guid backupId) 49 | : base(originalServicePartitionId, timeStampUtc, backupOption, backupId) 50 | { 51 | BlockBlobUri = blockBlobUri ?? throw new ArgumentNullException(nameof(blockBlobUri)); 52 | } 53 | } 54 | 55 | public class BlobStorageUri 56 | { 57 | /// 58 | /// Initializes a new instance using the primary endpoint for the storage account. 59 | /// 60 | /// The azure storage URI. 61 | /// storageUri 62 | 63 | public BlobStorageUri(StorageUri storageUri) 64 | { 65 | if (storageUri == null) 66 | { 67 | throw new ArgumentNullException(nameof(storageUri)); 68 | } 69 | 70 | PrimaryUri = storageUri.PrimaryUri; 71 | SecondaryUri = storageUri.SecondaryUri; 72 | } 73 | 74 | /// 75 | /// Creates a new default instance. 76 | /// 77 | public BlobStorageUri() 78 | { } 79 | 80 | /// 81 | /// The endpoint for the primary location for the storage account. 82 | /// 83 | public Uri PrimaryUri { get; set; } 84 | /// 85 | /// The endpoint for the secondary location for the storage account. 86 | /// 87 | public Uri SecondaryUri { get; set; } 88 | } 89 | } -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/IBackupRestoreService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Fabric; 3 | using System.Threading.Tasks; 4 | using Microsoft.ServiceFabric.Data; 5 | using Microsoft.ServiceFabric.Services.Remoting; 6 | 7 | namespace ServiceFabric.BackupRestore 8 | { 9 | /// 10 | /// This interface must be implemented to interact with the backup & restore operations of a service. 11 | /// 12 | public interface IBackupRestoreService : IService 13 | { 14 | /// 15 | /// Asynchronously starts the creation of a backup of the state of this replica and stores that into the central store. 16 | /// This method completes and returns before the backup process is completely done. 17 | /// 18 | /// 19 | Task BeginCreateBackup(BackupOption backupOption); 20 | 21 | /// 22 | /// Asynchronously starts a restore operation using the state indicated by . 23 | /// The backup is retrieved from the central store. 24 | /// This method completes and returns before the backup restore process is completely done. 25 | /// 26 | /// 27 | Task BeginRestoreBackup(BackupMetadata backupMetadata, DataLossMode dataLossMode); 28 | 29 | /// 30 | /// Lists all centrally stored backups for the service, inside the CentralBackupStore. 31 | /// 32 | /// 33 | Task> ListBackups(); 34 | 35 | /// 36 | /// Lists all centrally stored backups present for the service, inside the CentralBackupStore. 37 | /// 38 | /// 39 | Task> ListAllBackups(); 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/IBackupRestoreServiceOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Fabric; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | 7 | namespace ServiceFabric.BackupRestore 8 | { 9 | /// 10 | /// This interface must be implemented to perform backups and restores. 11 | /// Implement this service to enable calls to 12 | /// Sample implementation in and . 13 | /// 14 | public interface IBackupRestoreServiceOperations 15 | { 16 | /// 17 | /// Gets the implementation of 18 | /// 19 | ICentralBackupStore CentralBackupStore { get; } 20 | /// 21 | /// Gets the Stateful Sevice Context. 22 | /// 23 | StatefulServiceContext Context { get; } 24 | /// 25 | /// Gets an optional log callback 26 | /// 27 | Action LogCallback { get; } 28 | /// 29 | /// Get the Stateful Service Partition 30 | /// 31 | IStatefulServicePartition Partition { get; } 32 | /// 33 | /// Gets a callback that will be invoked after a local backup has been created. 34 | /// 35 | /// 36 | /// 37 | /// 38 | Task PostBackupCallbackAsync(BackupInfo backupInfo, CancellationToken cancellationToken); 39 | /// 40 | /// Begins a local backup operation 41 | /// 42 | /// 43 | /// 44 | Task BackupAsync(BackupDescription backupDescription); 45 | } 46 | } -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/ICentralBackupStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | 7 | namespace ServiceFabric.BackupRestore 8 | { 9 | /// 10 | /// Central store in which Service Partitions can store their backups. 11 | /// 12 | public interface ICentralBackupStore 13 | { 14 | /// 15 | /// Copies contents from the provided on the node, to a new backup folder in the central store. 16 | /// 17 | /// The source folder on the current Node. 18 | /// Indicates the type of backup. 19 | /// Partition Guid of replica 20 | /// 21 | /// 22 | Task UploadBackupFolderAsync(BackupOption backupOption, Guid servicePartitionId, string sourceDirectory, CancellationToken cancellationToken); 23 | 24 | 25 | /// 26 | /// Saves metadata for an uploaded backup. 27 | /// 28 | /// The destination folder. 29 | /// The information. 30 | /// The cancellation token. 31 | /// 32 | Task StoreBackupMetadataAsync(string destinationFolder, BackupMetadata info, CancellationToken cancellationToken = default(CancellationToken)); 33 | 34 | /// 35 | /// Copies contents from the last known backup folder to the provided on the node. 36 | /// 37 | /// 38 | /// 39 | /// 40 | /// 41 | Task DownloadBackupFolderAsync(Guid backupId, string destinationDirectory, CancellationToken cancellationToken); 42 | 43 | /// 44 | /// Lists all known backup metadata. 45 | /// 46 | /// 47 | Task> GetBackupMetadataAsync(Guid? backupId = null, Guid? servicePartitionId = null); 48 | 49 | /// 50 | /// Schedules a backup to be restored. 51 | /// 52 | /// 53 | /// 54 | /// 55 | [Obsolete("Naming issue. Call 'ScheduleBackupRestoreAsync'.")] 56 | Task ScheduleBackupAsync(Guid servicePartitionId, Guid backupId); 57 | 58 | /// 59 | /// Schedules a backup to be restored. 60 | /// 61 | /// 62 | /// 63 | /// 64 | Task ScheduleBackupRestoreAsync(Guid servicePartitionId, Guid backupId); 65 | 66 | /// 67 | /// Returns the id of a scheduled backup to be restored, for the provided or null. 68 | /// 69 | /// 70 | /// backupId 71 | Task RetrieveScheduledBackupAsync(Guid servicePartitionId); 72 | } 73 | } -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("ServiceFabric.BackupRestore")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("42be1897-995d-4466-9e7c-7a3ec9035ef9")] 20 | 21 | [assembly: InternalsVisibleTo("ServiceFabric.BackupRestore.Tests")] 22 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | FileSystem 8 | Release 9 | net462 10 | C:\LocalPackagesSource 11 | 12 | -------------------------------------------------------------------------------- /src/ServiceFabric.BackupRestore/ServiceFabric.BackupRestore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ServiceFabric.BackupRestore adds backup and restore features to your Reliable Stateful Services in Service Fabric. How to use this package: https://github.com/loekd/ServiceFabric.BackupRestore/blob/master/README.md 5 | 2018 6 | ServiceFabric.BackupRestore 7 | Loek Duys 8 | 3.1.3 9 | net452;net462;netstandard2.0 10 | x64 11 | ServiceFabric.BackupRestore 12 | ServiceFabric.BackupRestore 13 | ServiceFabric;Service;Fabric;Stateful;Reliable;Backup;Restore 14 | https://github.com/loekd/ServiceFabric.BackupRestore 15 | https://github.com/loekd/ServiceFabric.BackupRestore/blob/master/LICENSE.md 16 | true 17 | git 18 | https://github.com/loekd/ServiceFabric.BackupRestore.git 19 | false 20 | false 21 | false 22 | True 23 | True 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | true 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/ServiceFabric.BackupRestore.Tests/BackupRestoreServiceInternalExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | using Moq; 7 | 8 | namespace ServiceFabric.BackupRestore.Tests 9 | { 10 | [TestClass] 11 | public class BackupRestoreServiceInternalExtensionsTests 12 | { 13 | 14 | [TestMethod] 15 | public async Task TestGetBackupMetadataAsync_OneFullBackup_Present() 16 | { 17 | //arrange 18 | Guid partitionId = Guid.NewGuid(); 19 | DateTime now = DateTime.UtcNow; 20 | BackupMetadata backupMetadata = new BackupMetadata(partitionId, now, BackupOption.Full); 21 | 22 | var mockBackupStoreObject = new Moq.Mock(); 23 | var mockServiceObject = new Moq.Mock(); 24 | mockServiceObject.Setup(service => service.CentralBackupStore).Returns(mockBackupStoreObject.Object); 25 | mockServiceObject.Setup(service => service.Context).Returns(Mocks.MockStatefulServiceContextFactory.Default); 26 | 27 | //act 28 | var result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, backupMetadata); 29 | 30 | //assert 31 | Assert.AreEqual(1, result.Count); 32 | Assert.AreEqual(backupMetadata, result[0]); 33 | } 34 | 35 | 36 | 37 | [TestMethod] 38 | public async Task TestGetBackupMetadataAsync_OneFullBackup_AndOneIncrementalBackup_Present() 39 | { 40 | //arrange 41 | Guid partitionId = Guid.NewGuid(); 42 | DateTime now = DateTime.UtcNow; 43 | 44 | BackupMetadata backupMetadata = new BackupMetadata(partitionId, now, BackupOption.Full); 45 | BackupMetadata incrementalMetadata = new BackupMetadata(partitionId, now.AddHours(1), BackupOption.Incremental); 46 | 47 | var mockBackupStoreObject = new Moq.Mock(); 48 | mockBackupStoreObject 49 | .Setup(store => store.RetrieveScheduledBackupAsync(It.IsAny())) 50 | .Returns(Task.FromResult(incrementalMetadata)); 51 | 52 | mockBackupStoreObject 53 | .Setup(store => store.GetBackupMetadataAsync(null, It.IsAny())) 54 | .Returns(Task.FromResult>(new[] { backupMetadata, incrementalMetadata })); 55 | 56 | var mockServiceObject = new Moq.Mock(); 57 | mockServiceObject.Setup(service => service.CentralBackupStore).Returns(mockBackupStoreObject.Object); 58 | mockServiceObject.Setup(service => service.Context).Returns(Mocks.MockStatefulServiceContextFactory.Default); 59 | 60 | //act 61 | var result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, incrementalMetadata); 62 | 63 | //assert 64 | Assert.AreEqual(2, result.Count); 65 | Assert.AreEqual(backupMetadata, result[0]); 66 | Assert.AreEqual(incrementalMetadata, result[1]); 67 | } 68 | 69 | [TestMethod] 70 | public async Task TestGetBackupMetadataAsync_OneFullBackup_AndTwoIncrementalBackup_Present() 71 | { 72 | //arrange 73 | Guid partitionId = Guid.NewGuid(); 74 | DateTime now = DateTime.UtcNow; 75 | 76 | BackupMetadata backupMetadata = new BackupMetadata(partitionId, now, BackupOption.Full); 77 | BackupMetadata incrementalMetadata = new BackupMetadata(partitionId, now.AddHours(1), BackupOption.Incremental); 78 | BackupMetadata incrementalMetadataTwo = new BackupMetadata(partitionId, now.AddHours(2), BackupOption.Incremental); 79 | 80 | var mockBackupStoreObject = new Moq.Mock(); 81 | mockBackupStoreObject 82 | .Setup(store => store.RetrieveScheduledBackupAsync(It.IsAny())) 83 | .Returns(Task.FromResult(incrementalMetadata)); 84 | 85 | mockBackupStoreObject 86 | .Setup(store => store.GetBackupMetadataAsync(null, It.IsAny())) 87 | .Returns(Task.FromResult>(new[] { backupMetadata, incrementalMetadata, incrementalMetadataTwo })); 88 | 89 | var mockServiceObject = new Moq.Mock(); 90 | mockServiceObject.Setup(service => service.CentralBackupStore).Returns(mockBackupStoreObject.Object); 91 | mockServiceObject.Setup(service => service.Context).Returns(Mocks.MockStatefulServiceContextFactory.Default); 92 | 93 | //act 94 | var result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, incrementalMetadataTwo); 95 | 96 | //assert 97 | Assert.AreEqual(3, result.Count); 98 | Assert.AreEqual(backupMetadata, result[0]); 99 | Assert.AreEqual(incrementalMetadata, result[1]); 100 | Assert.AreEqual(incrementalMetadataTwo, result[2]); 101 | } 102 | 103 | 104 | [TestMethod] 105 | public async Task TestGetBackupMetadataAsync_TwoFullBackups_AndFourIncrementalBackup_Present() 106 | { 107 | //arrange 108 | Guid partitionId = Guid.NewGuid(); 109 | DateTime now = DateTime.UtcNow; 110 | 111 | BackupMetadata backupMetadata = new BackupMetadata(partitionId, now, BackupOption.Full); 112 | BackupMetadata incrementalMetadata = new BackupMetadata(partitionId, now.AddHours(1), BackupOption.Incremental); 113 | BackupMetadata incrementalMetadataTwo = new BackupMetadata(partitionId, now.AddHours(2), BackupOption.Incremental); 114 | 115 | BackupMetadata backupMetadataTwo = new BackupMetadata(partitionId, now.AddDays(1), BackupOption.Full); 116 | BackupMetadata incrementalMetadataThree = new BackupMetadata(partitionId, now.AddDays(1).AddHours(1), BackupOption.Incremental); 117 | BackupMetadata incrementalMetadataFour = new BackupMetadata(partitionId, now.AddDays(1).AddHours(2), BackupOption.Incremental); 118 | 119 | 120 | var mockBackupStoreObject = new Moq.Mock(); 121 | mockBackupStoreObject 122 | .Setup(store => store.RetrieveScheduledBackupAsync(It.IsAny())) 123 | .Returns(Task.FromResult(incrementalMetadata)); 124 | 125 | mockBackupStoreObject 126 | .Setup(store => store.GetBackupMetadataAsync(null, It.IsAny())) 127 | .Returns(Task.FromResult>(new[] 128 | { 129 | backupMetadata, 130 | incrementalMetadata, 131 | incrementalMetadataTwo, 132 | backupMetadataTwo, 133 | incrementalMetadataThree, 134 | incrementalMetadataFour 135 | })); 136 | 137 | var mockServiceObject = new Moq.Mock(); 138 | mockServiceObject.Setup(service => service.CentralBackupStore).Returns(mockBackupStoreObject.Object); 139 | mockServiceObject.Setup(service => service.Context).Returns(Mocks.MockStatefulServiceContextFactory.Default); 140 | 141 | //act 142 | var result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, incrementalMetadataTwo); 143 | 144 | //assert 145 | Assert.AreEqual(3, result.Count); 146 | Assert.AreEqual(backupMetadata, result[0]); 147 | Assert.AreEqual(incrementalMetadata, result[1]); 148 | Assert.AreEqual(incrementalMetadataTwo, result[2]); 149 | 150 | //act 151 | result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, incrementalMetadataFour); 152 | 153 | //assert 154 | Assert.AreEqual(3, result.Count); 155 | Assert.AreEqual(backupMetadataTwo, result[0]); 156 | Assert.AreEqual(incrementalMetadataThree, result[1]); 157 | Assert.AreEqual(incrementalMetadataFour, result[2]); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /test/ServiceFabric.BackupRestore.Tests/BlobStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using System.Linq; 8 | using Microsoft.WindowsAzure.Storage.Blob; 9 | 10 | namespace ServiceFabric.BackupRestore.Tests 11 | { 12 | [TestClass] 13 | public class BlobStoreTests 14 | { 15 | private static readonly string Remote = Path.Combine(Path.GetTempPath(), "BlobStoreTests", "Remote"); 16 | private static readonly string Local = Path.Combine(Path.GetTempPath(), "BlobStoreTests", "Local"); 17 | private const string ContainerName = "servicefabricbackups"; 18 | 19 | [ClassCleanup] 20 | public static void ClassCleanup() 21 | { 22 | Cleanup(); 23 | } 24 | 25 | private static void Cleanup() 26 | { 27 | // ReSharper disable EmptyGeneralCatchClause 28 | 29 | try 30 | { 31 | var store = new BlobStore("UseDevelopmentStorage=true", ContainerName); 32 | store.InitializeAsync().ConfigureAwait(false).GetAwaiter().GetResult(); 33 | store.DeleteContainer(); 34 | } 35 | catch 36 | { 37 | 38 | } 39 | try 40 | { 41 | Directory.Delete(Local, true); 42 | } 43 | catch 44 | { 45 | 46 | } 47 | } 48 | 49 | [TestInitialize] 50 | public void ClassInitialize() 51 | { 52 | Cleanup(); 53 | } 54 | 55 | [TestMethod] 56 | public void TestCtor() 57 | { 58 | var store = new BlobStore("UseDevelopmentStorage=true", ContainerName); 59 | Assert.IsInstanceOfType(store, typeof(ICentralBackupStore)); 60 | } 61 | 62 | [TestMethod] 63 | public void TestCtorFail() 64 | { 65 | // ReSharper disable UnusedVariable 66 | 67 | Assert.ThrowsException(() => 68 | { 69 | var store = new BlobStore((string)null, ContainerName); 70 | }); 71 | 72 | Assert.ThrowsException(() => 73 | { 74 | var store = new BlobStore((CloudBlobClient)null, ContainerName); 75 | }); 76 | 77 | Assert.ThrowsException(() => 78 | { 79 | var store = new BlobStore(string.Empty, ContainerName); 80 | }); 81 | 82 | Assert.ThrowsException(() => 83 | { 84 | var store = new BlobStore(" ", ContainerName); 85 | }); 86 | } 87 | 88 | 89 | [TestMethod] 90 | public async Task TestUploadBackupFolderAsync() 91 | { 92 | if (!CanOperate()) 93 | { 94 | Assert.Inconclusive("Can't run at this machine!"); 95 | } 96 | 97 | //setup 98 | const string content = "content"; 99 | const string testTxt = "test.txt"; 100 | const string subFolderName = "Sub"; 101 | 102 | var partitionId = Guid.Parse("{92DA7CA2-CE18-497C-84DB-428B8C476994}"); 103 | string partitionFolder = partitionId.ToString("n"); 104 | 105 | //prepare a folder and a file in the local store: 106 | Directory.CreateDirectory(Local); 107 | File.WriteAllText(Path.Combine(Local, testTxt), content); 108 | 109 | string subFolder = Path.Combine(Local, subFolderName); 110 | Directory.CreateDirectory(subFolder); 111 | File.WriteAllText(Path.Combine(subFolder, testTxt), content); 112 | 113 | BlobStore store = new BlobStore("UseDevelopmentStorage=true", ContainerName); 114 | await store.InitializeAsync(); 115 | //act 116 | var result = await store.UploadBackupFolderAsync(BackupOption.Full, partitionId, Local, CancellationToken.None); 117 | 118 | //asserts 119 | Assert.IsInstanceOfType(result, typeof(BackupMetadata)); 120 | Assert.AreEqual(partitionId, result.OriginalServicePartitionId); 121 | Assert.AreNotEqual(Guid.Empty, result.BackupId); 122 | 123 | var container = store.BlobClient.GetContainerReference(ContainerName); 124 | Assert.IsTrue(container.ExistsAsync().ConfigureAwait(false).GetAwaiter().GetResult()); 125 | 126 | var rootFolder = container.GetDirectoryReference(BlobStore.RootFolder); 127 | 128 | var list = rootFolder.ListBlobsSegmentedAsync(true, BlobListingDetails.All, 10, new BlobContinuationToken(), null, null) 129 | .ConfigureAwait(false) 130 | .GetAwaiter() 131 | .GetResult() 132 | .Results 133 | .ToList(); 134 | Assert.AreEqual(2, list.Count(file => file.Uri.AbsoluteUri.EndsWith(testTxt))); 135 | Assert.AreEqual(1, list.Count(file => file.Uri.AbsoluteUri.EndsWith($"{subFolderName}/{testTxt}"))); 136 | Assert.AreEqual(1, list.Count(file => file.Uri.AbsoluteUri.EndsWith(FileStore.ServiceFabricBackupRestoreMetadataFileName))); 137 | } 138 | 139 | 140 | [TestMethod] 141 | public async Task TestDownloadBackupFolderAsync() 142 | { 143 | if (!CanOperate()) 144 | { 145 | Assert.Inconclusive("Can't run at this machine!"); 146 | } 147 | 148 | //setup 149 | const string content = "content"; 150 | const string testTxt = "test.txt"; 151 | const string subFolderName = "Sub"; 152 | 153 | var partitionId = Guid.Parse("{92DA7CA2-CE18-497C-84DB-428B8C476994}"); 154 | string partitionFolder = partitionId.ToString("n"); 155 | 156 | //prepare a folder and a file in the local store: 157 | Directory.CreateDirectory(Local); 158 | File.WriteAllText(Path.Combine(Local, testTxt), content); 159 | 160 | string subFolder = Path.Combine(Local, subFolderName); 161 | Directory.CreateDirectory(subFolder); 162 | File.WriteAllText(Path.Combine(subFolder, testTxt), content); 163 | 164 | BlobStore store = new BlobStore("UseDevelopmentStorage=true", ContainerName); 165 | await store.InitializeAsync(); 166 | //act 167 | var result = await store.UploadBackupFolderAsync(BackupOption.Full, partitionId, Local, CancellationToken.None); 168 | Guid backupId = result.BackupId; 169 | await store.DownloadBackupFolderAsync(backupId, Local, CancellationToken.None); 170 | 171 | //asserts 172 | 173 | Assert.IsTrue(Directory.Exists(Path.Combine(Local))); 174 | Assert.IsTrue(File.Exists(Path.Combine(Local, testTxt))); 175 | Assert.IsTrue(Directory.Exists(Path.Combine(Local, subFolderName))); 176 | Assert.IsTrue(File.Exists(Path.Combine(Local, subFolderName, testTxt))); 177 | 178 | Assert.AreEqual(content, File.ReadAllText(Path.Combine(Local, testTxt))); 179 | Assert.AreEqual(content, File.ReadAllText(Path.Combine(Local, subFolderName, testTxt))); 180 | } 181 | 182 | private bool CanOperate() 183 | { 184 | try 185 | { 186 | return Directory.Exists(@"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator"); 187 | } 188 | catch 189 | { 190 | return false; 191 | } 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /test/ServiceFabric.BackupRestore.Tests/FileStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.ServiceFabric.Data; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace ServiceFabric.BackupRestore.Tests 9 | { 10 | [TestClass] 11 | public class FileStoreTests 12 | { 13 | private static readonly string Remote = Path.Combine(Path.GetTempPath(), "FileStoreTests", "Remote"); 14 | private static readonly string Local = Path.Combine(Path.GetTempPath(), "FileStoreTests", "Local"); 15 | 16 | [ClassCleanup] 17 | public static void ClassCleanup() 18 | { 19 | Cleanup(); 20 | } 21 | 22 | private static void Cleanup() 23 | { 24 | // ReSharper disable EmptyGeneralCatchClause 25 | 26 | try 27 | { 28 | Directory.Delete(Remote, true); 29 | } 30 | catch 31 | { 32 | 33 | } 34 | try 35 | { 36 | Directory.Delete(Local, true); 37 | } 38 | catch 39 | { 40 | 41 | } 42 | } 43 | 44 | [TestInitialize] 45 | public void ClassInitialize() 46 | { 47 | Cleanup(); 48 | } 49 | 50 | [TestMethod] 51 | public void TestCtor() 52 | { 53 | var store = new FileStore(@"c:\temp"); 54 | Assert.IsInstanceOfType(store, typeof(ICentralBackupStore)); 55 | } 56 | 57 | [TestMethod] 58 | public void TestCtorFail() 59 | { 60 | // ReSharper disable UnusedVariable 61 | 62 | Assert.ThrowsException(() => 63 | { 64 | var store = new FileStore(null); 65 | }); 66 | 67 | Assert.ThrowsException(() => 68 | { 69 | var store = new FileStore(string.Empty); 70 | }); 71 | 72 | Assert.ThrowsException(() => 73 | { 74 | var store = new FileStore(" "); 75 | }); 76 | } 77 | 78 | 79 | [TestMethod] 80 | public async Task TestUploadBackupFolderAsync() 81 | { 82 | //setup 83 | const string content = "content"; 84 | const string testTxt = "test.txt"; 85 | const string subFolderName = "Sub"; 86 | 87 | var partitionId = Guid.NewGuid(); 88 | string partitionFolder = partitionId.ToString("n"); 89 | 90 | //prepare a folder and a file in the local store: 91 | Directory.CreateDirectory(Local); 92 | File.WriteAllText(Path.Combine(Local, testTxt), content); 93 | 94 | string subFolder = Path.Combine(Local, subFolderName); 95 | Directory.CreateDirectory(subFolder); 96 | File.WriteAllText(Path.Combine(subFolder, testTxt), content); 97 | 98 | var store = new FileStore(Remote); 99 | 100 | //act 101 | var result = await store.UploadBackupFolderAsync(BackupOption.Full, partitionId, Local, CancellationToken.None); 102 | string dateTimeFolder = store.CreateDateTimeFolderName(partitionId, result.TimeStampUtc); 103 | 104 | //asserts 105 | Assert.IsInstanceOfType(result, typeof(BackupMetadata)); 106 | Assert.AreEqual(partitionId, result.OriginalServicePartitionId); 107 | Assert.AreNotEqual(Guid.Empty, result.BackupId); 108 | 109 | Assert.IsTrue(Directory.Exists(Path.Combine(Remote))); 110 | Assert.IsTrue(File.Exists(Path.Combine(dateTimeFolder, testTxt))); 111 | Assert.IsTrue(Directory.Exists(Path.Combine(dateTimeFolder, subFolderName))); 112 | Assert.IsTrue(File.Exists(Path.Combine(dateTimeFolder, subFolderName, testTxt))); 113 | 114 | Assert.AreEqual(content, File.ReadAllText(Path.Combine(dateTimeFolder, testTxt))); 115 | Assert.AreEqual(content, File.ReadAllText(Path.Combine(dateTimeFolder, subFolderName, testTxt))); 116 | } 117 | 118 | 119 | [TestMethod] 120 | public async Task TestDownloadBackupFolderAsync() 121 | { 122 | //setup 123 | const string content = "content"; 124 | const string testTxt = "test.txt"; 125 | const string subFolderName = "Sub"; 126 | 127 | Guid partitionId = Guid.NewGuid(); 128 | Guid backupId = Guid.NewGuid(); 129 | var timeStampUtc = DateTime.UtcNow; 130 | 131 | 132 | //prepare a remote folder and a file in the store: 133 | var store = new FileStore(Remote); 134 | 135 | Directory.CreateDirectory(Remote); 136 | string backupFolder = store.CreateDateTimeFolderName(partitionId, timeStampUtc); 137 | Directory.CreateDirectory(backupFolder); 138 | File.WriteAllText(Path.Combine(backupFolder, testTxt), content); 139 | 140 | string subFolder = Path.Combine(backupFolder, subFolderName); 141 | Directory.CreateDirectory(subFolder); 142 | File.WriteAllText(Path.Combine(subFolder, testTxt), content); 143 | 144 | 145 | 146 | //save backup metadata 147 | await store.StoreBackupMetadataAsync(backupFolder, new BackupMetadata(partitionId, timeStampUtc, BackupOption.Full, backupId)); 148 | 149 | //fake SF backup metadata 150 | File.WriteAllText(Path.Combine(backupFolder, FileStore.BackupMetadataFileName), ""); 151 | 152 | //act 153 | await store.DownloadBackupFolderAsync(backupId, Local, CancellationToken.None); 154 | 155 | //asserts 156 | 157 | Assert.IsTrue(Directory.Exists(Path.Combine(Local))); 158 | Assert.IsTrue(File.Exists(Path.Combine(Local, testTxt))); 159 | Assert.IsTrue(Directory.Exists(Path.Combine(Local, subFolderName))); 160 | Assert.IsTrue(File.Exists(Path.Combine(Local, subFolderName, testTxt))); 161 | 162 | Assert.AreEqual(content, File.ReadAllText(Path.Combine(Local, testTxt))); 163 | Assert.AreEqual(content, File.ReadAllText(Path.Combine(Local, subFolderName, testTxt))); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /test/ServiceFabric.BackupRestore.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("ServiceFabric.BackupRestore.Tests")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("6cd12a6a-6136-4354-ac9b-4f0a6224de8c")] 20 | -------------------------------------------------------------------------------- /test/ServiceFabric.BackupRestore.Tests/ServiceFabric.BackupRestore.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | Unit tests 9 | 2018 10 | ServiceFabric.BackupRestore.Tests 11 | 3.1.3 12 | Loek Duys 13 | net462;netcoreapp2.0 14 | x64 15 | ServiceFabric.BackupRestore.Tests 16 | ServiceFabric.BackupRestore.Tests 17 | true 18 | false 19 | false 20 | false 21 | 22 | exe 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | --------------------------------------------------------------------------------