Skip to content

Commit

Permalink
Cleaned up a bunch of stuff around starting up new repos. Getting clo…
Browse files Browse the repository at this point in the history
…ser to grace init and grace connect. Implemented physical deletes on actors. Moved most server-side services to the Actors project. Implemented a Dispose pattern to handle deleted actors. So much other clean-up.
  • Loading branch information
ScottArbeit committed Sep 26, 2023
1 parent 6735b33 commit 24cb743
Show file tree
Hide file tree
Showing 53 changed files with 3,524 additions and 1,318 deletions.
295 changes: 295 additions & 0 deletions Assets/4-cube_t12.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,287 changes: 1,287 additions & 0 deletions Assets/8-cube.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions docs/The potential for misusing Grace.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# The potential for misusing Grace

Something I've thought about since the beginning of designing Grace is: how do we prevent ignorant and/or malicious management from using eventing data from Grace in the wrong way?
Something I've thought about since the beginning of designing Grace is: how do we prevent ignorant and/or malicious management from using event data from Grace in the wrong way?

So, to start, and just to be clear:

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

Expand All @@ -10,11 +12,11 @@ I'm aware of the potential to use things like "how often did this person do a sa

And, because I've been a programmer for several decades, I, like you, am also aware of how fucking stupid and shortsighted it would be to do so.

It's not my intention for Grace to become a monitoring platform. And there are features that Grace is meant to enable that simply can't happen without a normal, modern, cloud-native event-driven architecture.
It's not my intention for Grace to become a monitoring platform. And yet, there are features that Grace enables that simply can't happen without a modern, cloud-native event-driven architecture.

![](https://gracevcsdevelopment.blob.core.windows.net/static/Orange3.svg)

## Even Git has timestamps
## Git has timestamps, too

Sure, Git is distributed, and there's no way for the server to reach into your local repo and get status. If you use the feature-branch-gets-deleted method of using Git, then you're still pushing your feature branch and its commits up to the server before the merge-and-delete, and so you have that opportunity in Git to get some similar information that we're worried about in Grace. We haven't, as an industry, weaponized this data yet, so I'm hopeful we won't with any future version control system.

Expand Down
16 changes: 8 additions & 8 deletions src/.grace/graceconfig.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"OwnerId": "7d45a98f-b802-4d52-a890-c41eb35cb623",
"OwnerName": "Owner0008SuperLongNameToSeeWhatHappensToOutputFormatting",
"OrganizationId": "71f736b4-06dd-4cef-a4e2-b4b2f859fb9c",
"OrganizationName": "OrgDDAB",
"RepositoryId": "bd5b2b65-293a-4457-9399-d4fe6d4f4406",
"RepositoryName": "RepoDDAB",
"BranchId": "e4def31b-4547-4f6b-9324-56eba666b4b2",
"BranchName": "BranchDDAB",
"OwnerId": "c911bc8a-8ba8-47a7-a694-1fbd8f80ab32",
"OwnerName": "Owner001",
"OrganizationId": "df1e97e4-5ee9-4e37-b095-4a677b44ef57",
"OrganizationName": "Organization001",
"RepositoryId": "8c4eae5f-f734-4067-a39f-ce2d9c9fabbc",
"RepositoryName": "Repository001",
"BranchId": "de9220fb-e2bc-4562-b5c4-1798d81100c1",
"BranchName": "main",
"DefaultBranchName": "main",
"Themes": [
{
Expand Down
69 changes: 51 additions & 18 deletions src/Grace.Actors/Branch.Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,24 @@ module Branch =
let mutable actorStartTime = Instant.MinValue
let mutable logScope: IDisposable = null
let mutable currentCommand = String.Empty

/// Indicates that the actor is in an undefined state, and should be reset.
let mutable isDisposed = false

let updateDto branchEventType currentBranchDto =
let newOrganizationDto =
match branchEventType with
| Created (branchId, branchName, parentBranchId, basedOn, repositoryId) -> {BranchDto.Default with BranchId = branchId; BranchName = branchName; ParentBranchId = parentBranchId; BasedOn = basedOn; RepositoryId = repositoryId}
| Created (branchId, branchName, parentBranchId, basedOn, repositoryId, initialPermissions) ->
let mutable branchDto = {BranchDto.Default with BranchId = branchId; BranchName = branchName; ParentBranchId = parentBranchId; BasedOn = basedOn; RepositoryId = repositoryId}
for referenceType in initialPermissions do
branchDto <- match referenceType with
| ReferenceType.Promotion -> {branchDto with PromotionEnabled = true}
| ReferenceType.Commit -> {branchDto with CommitEnabled = true}
| ReferenceType.Checkpoint -> {branchDto with CheckpointEnabled = true}
| ReferenceType.Save -> {branchDto with SaveEnabled = true}
| ReferenceType.Tag -> {branchDto with TagEnabled = true}
branchDto

| Rebased referenceId -> {currentBranchDto with BasedOn = referenceId}
| NameSet branchName -> {currentBranchDto with BranchName = branchName}
| Promoted (referenceId, directoryVersion, sha256Hash, referenceText) -> {currentBranchDto with LatestPromotion = referenceId}
Expand All @@ -58,9 +71,9 @@ module Branch =
| EnabledSave enabled -> {currentBranchDto with SaveEnabled = enabled}
| EnabledTag enabled -> {currentBranchDto with TagEnabled = enabled}
| ReferenceRemoved _ -> currentBranchDto
| LogicalDeleted -> {currentBranchDto with DeletedAt = Some (getCurrentInstant())}
| LogicalDeleted (force, deleteReason) -> {currentBranchDto with DeletedAt = Some (getCurrentInstant()); DeleteReason = deleteReason}
| PhysicalDeleted -> currentBranchDto // Do nothing because it's about to be deleted anyway.
| Undeleted -> {currentBranchDto with DeletedAt = None}
| Undeleted -> {currentBranchDto with DeletedAt = None; DeleteReason = String.Empty}

{newOrganizationDto with UpdatedAt = Some (getCurrentInstant())}

Expand Down Expand Up @@ -103,6 +116,13 @@ module Branch =
logScope <- log.BeginScope("Actor {actorName}", actorName)
currentCommand <- String.Empty
//log.LogInformation("{CurrentInstant}: Started {ActorName}.{MethodName} Id: {Id}.", getCurrentInstantExtended(), actorName, context.MethodName, this.Id.GetId())

// This checks if the actor is still active, but in an undefined state, which will _almost_ never happen.
// isDisposed is set when the actor is deleted, or if an error occurs where we're not sure of the state and want to reload from the database.
if isDisposed then
this.OnActivateAsync().Wait()
isDisposed <- false

Task.CompletedTask

override this.OnPostActorMethodAsync(context) =
Expand Down Expand Up @@ -154,12 +174,8 @@ module Branch =
return Error graceError
}

member private this.SchedulePhysicalDeletion() =
// Send pub/sub message to kick off branch deletion
// Will include: enumerating and deleting each save, checkpoint, commit, and tag.
task {
return ()
}
member private this.SchedulePhysicalDeletion(deleteReason) =
this.RegisterReminderAsync(ReminderType.PhysicalDeletion, convertToByteArray deleteReason, Constants.DefaultPhysicalDeletionReminderTime, TimeSpan.FromMilliseconds(-1)).Wait()

interface IBranchActor with
member this.Exists() =
Expand All @@ -177,7 +193,7 @@ module Branch =
return Error (GraceError.Create (BranchError.getErrorMessage DuplicateCorrelationId) metadata.CorrelationId)
else
match command with
| BranchCommand.Create (branchId, branchName, parentBranchId, basedOn, repositoryId) ->
| BranchCommand.Create (branchId, branchName, parentBranchId, basedOn, repositoryId, branchPermissions) ->
match branchDto.UpdatedAt with
| Some _ -> return Error (GraceError.Create (BranchError.getErrorMessage BranchAlreadyExists) metadata.CorrelationId)
| None -> return Ok command
Expand All @@ -203,8 +219,8 @@ module Branch =
let! event =
task {
match command with
| Create (branchId, branchName, parentBranchId, basedOn, repositoryId) ->
return Created(branchId, branchName, parentBranchId, basedOn, repositoryId)
| Create (branchId, branchName, parentBranchId, basedOn, repositoryId, branchPermissions) ->
return Created(branchId, branchName, parentBranchId, basedOn, repositoryId, branchPermissions)
| Rebase referenceId -> return Rebased referenceId
| SetName organizationName -> return NameSet (organizationName)
| BranchCommand.Promote (directoryId, sha256Hash, referenceText) ->
Expand Down Expand Up @@ -253,13 +269,11 @@ module Branch =
| EnableSave enabled -> return EnabledSave enabled
| EnableTag enabled -> return EnabledTag enabled
| RemoveReference referenceId -> return ReferenceRemoved referenceId
| DeleteLogical ->
//do! this.UnregisterMaintenanceReminder()
return LogicalDeleted
| DeleteLogical (force, deleteReason) ->
this.SchedulePhysicalDeletion(deleteReason)
return LogicalDeleted (force, deleteReason)
| DeletePhysical ->
task {
do! this.SchedulePhysicalDeletion()
} |> Async.AwaitTask |> ignore
isDisposed <- true
return PhysicalDeleted
| Undelete -> return Undeleted
}
Expand Down Expand Up @@ -287,3 +301,22 @@ module Branch =
member this.GetLatestCommit() = branchDto.LatestCommit |> returnTask

member this.GetLatestPromotion() = branchDto.LatestPromotion |> returnTask

interface IRemindable with
override this.ReceiveReminderAsync(reminderName, state, dueTime, period) =
let stateManager = this.StateManager
match reminderName with
| ReminderType.Maintenance ->
task {
// Do some maintenance
()
} :> Task
| ReminderType.PhysicalDeletion ->
task {
let! deletedDtoState = stateManager.TryRemoveStateAsync(dtoStateName)
let! deletedEventsState = stateManager.TryRemoveStateAsync(eventsStateName)
isDisposed <- true
// TODO: Log these results.
return ()
} :> Task
| _ -> failwith "Unknown reminder type."
15 changes: 1 addition & 14 deletions src/Grace.Actors/BranchName.Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
open Dapr.Actors
open Dapr.Actors.Runtime
open Grace.Actors.Constants
open Grace.Actors.Interfaces
open Grace.Shared.Utilities
open Microsoft.Extensions.Logging
open NodaTime
Expand All @@ -14,20 +15,6 @@ module BranchName =
let mutable actorStartTime = Instant.MinValue
let mutable logScope: IDisposable = null

let GetActorId repositoryId branchName = ActorId($"{repositoryId}-{branchName}")

type IBranchNameActor =
inherit IActor
/// <summary>
/// Sets the BranchId that matches the BranchName.
/// </summary>
abstract member SetBranchId: branchName: string -> Task

/// <summary>
/// Returns the BranchId for the given BranchName.
/// </summary>
abstract member GetBranchId: unit -> Task<String option>

type BranchNameActor(host: ActorHost) =
inherit Actor(host)

Expand Down
11 changes: 3 additions & 8 deletions src/Grace.Actors/Commands.Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Commands =
module Branch =
[<KnownType("GetKnownTypes")>]
type BranchCommand =
| Create of branchId: BranchId * branchName: BranchName * parentBranchId: BranchId * basedOn: ReferenceId * repositoryId: RepositoryId
| Create of branchId: BranchId * branchName: BranchName * parentBranchId: BranchId * basedOn: ReferenceId * repositoryId: RepositoryId * initialPermissions: ReferenceType[]
| Rebase of basedOn: ReferenceId
| SetName of newName: BranchName
| Promote of directoryId: DirectoryId * sha256Hash: Sha256Hash * referenceText: ReferenceText
Expand All @@ -23,7 +23,7 @@ module Commands =
| EnableSave of enabled: bool
| EnableTag of enabled: bool
| RemoveReference of referenceId: ReferenceId
| DeleteLogical
| DeleteLogical of force: bool * deleteReason: string
| DeletePhysical
| Undelete
static member GetKnownTypes() = GetKnownTypes<BranchCommand>()
Expand All @@ -36,8 +36,6 @@ module Commands =
| SetType of organizationType: OrganizationType
| SetSearchVisibility of searchVisibility: SearchVisibility
| SetDescription of description: string
| AddRepository of repositoryId: RepositoryId * repositoryName: RepositoryName
| DeleteRepository of repositoryId: RepositoryId
| DeleteLogical of force: bool * deleteReason: string
| DeletePhysical
| Undelete
Expand All @@ -51,8 +49,6 @@ module Commands =
| SetType of ownerType: OwnerType
| SetSearchVisibility of searchVisibility: SearchVisibility
| SetDescription of description: string
| AddOrganization of organizationId: OrganizationId * organizationName: OrganizationName
| DeleteOrganization of organizationId: OrganizationId
| DeleteLogical of force: bool * deleteReason: string
| DeletePhysical
| Undelete
Expand All @@ -62,6 +58,7 @@ module Commands =
[<KnownType("GetKnownTypes")>]
type RepositoryCommand =
| Create of repositoryName: RepositoryName * repositoryId: RepositoryId * ownerId: OwnerId * organizationId: OrganizationId
| Initialize
| SetObjectStorageProvider of objectStorageProvider: ObjectStorageProvider
| SetStorageAccountName of storageAccountName: StorageAccountName
| SetStorageContainerName of storageContainerName: StorageContainerName
Expand All @@ -75,8 +72,6 @@ module Commands =
| SetDescription of description: string
| EnableSingleStepPromotion of enabled: bool
| EnableComplexPromotion of enabled: bool
| AddBranch of branchName: BranchName
| DeleteBranch of branchName: BranchName
| DeleteLogical of force: bool * deleteReason: string
| DeletePhysical
| Undelete
Expand Down
15 changes: 12 additions & 3 deletions src/Grace.Actors/Constants.Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ module Constants =
let Tag = "TagActor"
let User = "UserActor"

let defaultObjectStorageProvider = ObjectStorageProvider.AzureBlobStorage
let defaultObjectStorageAccount = "gracevcsdevelopment"
let defaultObjectStorageContainerName = "grace-objects"
module ReminderType =
[<Literal>]
let Maintenance = "Maintenance"
[<Literal>]
let PhysicalDeletion = "PhysicalDeletion"

let DefaultObjectStorageProvider = ObjectStorageProvider.AzureBlobStorage
let DefaultObjectStorageAccount = "gracevcsdevelopment"
let DefaultObjectStorageContainerName = "grace-objects"

// This will be TimeSpan.FromDays(7.0) in production, but for development purposes we'll use 30 seconds
let DefaultPhysicalDeletionReminderTime = TimeSpan.FromSeconds(30.0)
4 changes: 2 additions & 2 deletions src/Grace.Actors/Diff.Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ module Diff =
FileDiff.Create fileVersion1.RelativePath fileVersion2.Sha256Hash fileVersion1.CreatedAt fileVersion1.Sha256Hash fileVersion2.CreatedAt
(fileVersion1.IsBinary || fileVersion2.IsBinary) diffResults.InlineDiff diffResults.SideBySideOld diffResults.SideBySideNew
fileDiffs.Add(fileDiff)
| Add _ -> ()
| Delete _ -> ()
| Add -> ()
| Delete -> ()
})))


Expand Down
2 changes: 1 addition & 1 deletion src/Grace.Actors/DirectoryAppearance.Actor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ module DirectoryAppearance =
if dto.Appearances.Count = 0 then
let! deleteSucceeded = Storage.DeleteState stateManager dtoStateName
if deleteSucceeded then
let directoryVersionActorProxy = Services.ActorProxyFactory.CreateActorProxy<IDirectoryVersionActor>(this.Id, Constants.ActorName.DirectoryVersion)
let directoryVersionActorProxy = Services.actorProxyFactory.CreateActorProxy<IDirectoryVersionActor>(this.Id, Constants.ActorName.DirectoryVersion)
let! result = directoryVersionActorProxy.Delete(correlationId)
match result with
| Ok returnValue ->
Expand Down
Loading

0 comments on commit 24cb743

Please sign in to comment.