Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1-api-changes/WPB-26118
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`cellsInternal` in API version `v17` now exposes storage quotas as `totalLimitBytes` and `perUserQuotaBytes`. `totalLimitBytes` is optional for backward compatibility with existing records, any negative value for either field is accepted as unlimited, and unlimited values are returned as `-1`.
3 changes: 2 additions & 1 deletion charts/wire-server/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ galley:
collabora:
edition: COOL
storage:
perUserQuotaBytes: "1000000000000"
totalLimitBytes: "1000000000000"
perUserQuotaBytes: "-1"
allowedGlobalOperations:
status: enabled
config:
Expand Down
8 changes: 6 additions & 2 deletions docs/src/developer/reference/config-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,10 @@ cells:

### Cells Internal

Cells configuration is intentionally split: `cells` is controlled by the team admin, while `cellsInternal` is set by the site operator/customer support via the internal API only. For `cellsInternal`, the `status` and `lockStatus` fields are *required* to be set to `enabled` and `unlocked` respectively, as enforced by validation logic. Failure to set these values will result in a configuration error. This block holds the backend URL, Collabora edition, and a storage quota. The quota must be provided as a positive decimal string.
Cells configuration is intentionally split: `cells` is controlled by the team admin, while `cellsInternal` is set by the site operator/customer support via the internal API only. For `cellsInternal`, the `status` and `lockStatus` fields are *required* to be set to `enabled` and `unlocked` respectively, as enforced by validation logic. Failure to set these values will result in a configuration error. This block holds the backend URL, Collabora edition, and two storage quota settings:

- `totalLimitBytes` is the total team data limit. It is optional for backward compatibility with existing records, and any negative value is accepted as unlimited. The API returns `-1` for unlimited.
- `perUserQuotaBytes` is the per-user quota. Any negative value is accepted as unlimited. The API returns `-1` for unlimited.
Comment thread
battermann marked this conversation as resolved.

```yaml
# galley.yaml
Expand All @@ -678,7 +681,8 @@ config:
collabora:
edition: COOL
storage:
perUserQuotaBytes: "1000000000000" # 1 TB
totalLimitBytes: "1000000000000" # 1 TB
perUserQuotaBytes: "-1"
```

### Allowed Global Operations
Expand Down
3 changes: 2 additions & 1 deletion hack/helm_vars/wire-server/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,8 @@ galley:
collabora:
edition: COOL
storage:
perUserQuotaBytes: "1000000000000"
totalLimitBytes: "1000000000000"
perUserQuotaBytes: "-1"
allowedGlobalOperations:
status: enabled
config:
Expand Down
18 changes: 13 additions & 5 deletions integration/test/Test/FeatureFlags/CellsInternal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ testCellsInternalEvent :: (HasCallStack) => App ()
testCellsInternalEvent = do
(alice, tid, _) <- createTeam OwnDomain 0
q <- watchCellsEventsForTeam tid def
let quota = "234723984"
update = mkFt "enabled" "unlocked" defConf {quota}
let totalLimit = "234723984"
quota = "-1"
update = mkFt "enabled" "unlocked" defConf {totalLimit, quota}
setFeature InternalAPI alice tid "cellsInternal" update >>= assertSuccess
event <- getMessage q %. "payload.0"
event %. "name" `shouldMatch` "cellsInternal"
Expand All @@ -38,6 +39,7 @@ testCellsInternalEvent = do
event %. "data.status" `shouldMatch` "enabled"
event %. "data.config.backend.url" `shouldMatch` "https://cells-beta.wire.com"
event %. "data.config.collabora.edition" `shouldMatch` "COOL"
event %. "data.config.storage.totalLimitBytes" `shouldMatch` totalLimit
event %. "data.config.storage.perUserQuotaBytes" `shouldMatch` quota

testCellsInternal :: (HasCallStack) => App ()
Expand All @@ -59,14 +61,14 @@ validCellsInternalUpdates =
mkFt "enabled" "unlocked" defConf {collabora = "NO"},
mkFt "enabled" "unlocked" defConf {collabora = "COOL"},
mkFt "enabled" "unlocked" defConf {url = "https://wire.com"},
mkFt "enabled" "unlocked" defConf {totalLimit = "-1"},
mkFt "enabled" "unlocked" defConf {quota = "92346832946243"}
]

invalidCellsInternalUpdates :: [Value]
invalidCellsInternalUpdates =
[ mkFt "enabled" "unlocked" defConf {collabora = "FOO"},
mkFt "enabled" "unlocked" defConf {url = "http://wire.com"},
mkFt "enabled" "unlocked" defConf {quota = "-92346832946243"},
mkFt "enabled" "unlocked" defConf {quota = "1 TB"},
mkFt "disabled" "unlocked" defConf
]
Expand All @@ -81,7 +83,11 @@ mkFt s ls c =
.= object
[ "backend" .= object ["url" .= c.url],
"collabora" .= object ["edition" .= c.collabora],
"storage" .= object ["perUserQuotaBytes" .= c.quota]
"storage"
.= object
[ "totalLimitBytes" .= c.totalLimit,
"perUserQuotaBytes" .= c.quota
]
]
]

Expand All @@ -90,7 +96,8 @@ defConf =
CellsInternalConfig
{ url = "https://cells-beta.wire.com",
collabora = "COOL",
quota = "1000000000000"
totalLimit = "1000000000000",
quota = "-1"
}

testPatchCellsInternal :: (HasCallStack) => App ()
Expand All @@ -104,5 +111,6 @@ testPatchCellsInternal = do
data CellsInternalConfig = CellsInternalConfig
{ url :: String,
collabora :: String,
totalLimit :: String,
quota :: String
}
12 changes: 10 additions & 2 deletions integration/test/Test/FeatureFlags/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@ defAllFeatures =
.= object
[ "backend" .= object ["url" .= "https://cells-beta.wire.com"],
"collabora" .= object ["edition" .= "COOL"],
"storage" .= object ["perUserQuotaBytes" .= "1000000000000"]
"storage"
.= object
[ "totalLimitBytes" .= "1000000000000",
"perUserQuotaBytes" .= "-1"
]
]
],
"meetings" .= enabled,
Expand Down Expand Up @@ -439,7 +443,11 @@ defAllConfiguredFeatures =
.= object
[ "backend" .= object ["url" .= "https://cells-beta.wire.com"],
"collabora" .= object ["edition" .= "COOL"],
"storage" .= object ["perUserQuotaBytes" .= "1000000000000"]
"storage"
.= object
[ "totalLimitBytes" .= "1000000000000",
"perUserQuotaBytes" .= "-1"
]
]
]
),
Expand Down
53 changes: 47 additions & 6 deletions libs/wire-api/src/Wire/API/Team/Feature.hs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ module Wire.API.Team.Feature
CellsCollabora (..),
CollaboraEdition (..),
CellsBackend (..),
QuotaBytes (..),
CellsStorage (..),
NumBytes (..),
AllowedGlobalOperationsConfig (..),
Expand Down Expand Up @@ -168,13 +169,14 @@ import Data.Text.Encoding qualified as T
import Data.Text.Encoding.Error
import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.Encoding qualified as TL
import Data.Text.Read qualified as TR
import Data.Time
import Deriving.Aeson
import GHC.TypeLits
import Generics.SOP qualified as GSOP
import Imports hiding (All, First)
import Servant (FromHttpApiData (..), ToHttpApiData (..))
import Test.QuickCheck (choose, getPrintableString)
import Test.QuickCheck (choose, frequency, getPrintableString)
import Test.QuickCheck.Arbitrary (arbitrary)
import Test.QuickCheck.Gen (suchThat)
import URI.ByteString.QQ qualified as URI.QQ
Expand Down Expand Up @@ -1910,18 +1912,53 @@ instance ToSchema NumBytes where
pure n
)

newtype CellsStorage = CellsStorage
{ perUserQuotaBytes :: NumBytes
data QuotaBytes
= QuotaBytesFinite NumBytes
| QuotaBytesUnlimited
deriving stock (Show, Eq)
Comment thread
battermann marked this conversation as resolved.

instance Arbitrary QuotaBytes where
arbitrary =
frequency
[ (1, pure QuotaBytesUnlimited),
(9, QuotaBytesFinite <$> arbitrary)
]

instance ToSchema QuotaBytes where
schema = mkSchema quotaBytesDoc parseQuotaBytes quotaBytesToValue
where
quotaBytesDoc :: NamedSwaggerDoc
quotaBytesDoc = swaggerDoc @Text & S.schema . S.example ?~ "-1"

quotaBytesToValue :: QuotaBytes -> Maybe A.Value
quotaBytesToValue QuotaBytesUnlimited = Just $ A.String "-1"
quotaBytesToValue (QuotaBytesFinite n) = Just $ A.toJSON n

parseQuotaBytes :: A.Value -> A.Parser QuotaBytes
parseQuotaBytes = A.withText "QuotaBytes" $ \txt ->
case TR.signed TR.decimal txt :: Either String (Integer, Text) of
Left err -> fail err
Right (n, rest) -> do
unless (T.null rest) $
fail "value must be an integer string without decimals"
if n < 0
then pure QuotaBytesUnlimited
else QuotaBytesFinite <$> schemaParseJSON (A.String txt)

data CellsStorage = CellsStorage
{ totalLimitBytes :: Maybe QuotaBytes,
perUserQuotaBytes :: QuotaBytes
}
deriving (Show, Eq, Generic)
deriving (ToJSON, FromJSON, S.ToSchema) via Schema CellsStorage
deriving newtype (Arbitrary)
deriving (Arbitrary) via (GenericUniform CellsStorage)

instance ToSchema CellsStorage where
schema =
object $
CellsStorage
<$> perUserQuotaBytes .= field "perUserQuotaBytes" schema
<$> totalLimitBytes .= maybe_ (optField "totalLimitBytes" schema)
<*> perUserQuotaBytes .= field "perUserQuotaBytes" schema

data CellsInternalConfigB t f = CellsInternalConfig
{ backend :: Wear t f CellsBackend,
Expand Down Expand Up @@ -1955,7 +1992,11 @@ instance Default CellsInternalConfig where
CellsInternalConfig
{ backend = CellsBackend $ HttpsUrl [URI.QQ.uri|https://cells-beta.wire.com|],
collabora = CellsCollabora Cool,
storage = CellsStorage $ NumBytes $ BigIntString 1000000000000 -- 1 TB
storage =
CellsStorage
{ totalLimitBytes = Just $ QuotaBytesFinite $ NumBytes $ BigIntString 1000000000000, -- 1 TB
perUserQuotaBytes = QuotaBytesUnlimited
}
Comment thread
battermann marked this conversation as resolved.
}

instance (Typeable f, FieldF f) => ToSchema (CellsInternalConfigB Covered f) where
Expand Down
3 changes: 2 additions & 1 deletion services/galley/galley.integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ settings:
collabora:
edition: COOL
storage:
perUserQuotaBytes: "1000000000000"
totalLimitBytes: "1000000000000"
perUserQuotaBytes: "-1"
allowedGlobalOperations:
status: enabled
config:
Expand Down
9 changes: 8 additions & 1 deletion tools/stern/test/integration/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,9 @@ testCellsInternalConfig :: TestM ()
testCellsInternalConfig = do
(_, tid, _) <- createTeamWithNMembers 1
cfg <- getFeatureConfig @CellsInternalConfig tid
liftIO $ do
cfg.config.storage.totalLimitBytes @?= Just (QuotaBytesFinite (NumBytes (BigIntString 1000000000000)))
cfg.config.storage.perUserQuotaBytes @?= QuotaBytesUnlimited
let newBackend :: HttpsUrl
newBackend = fromMaybe (error "invalid url") . fromByteString $ "https://cells-internal.example.com"
newCfg =
Expand All @@ -407,7 +410,11 @@ testCellsInternalConfig = do
cfg.config
{ backend = CellsBackend newBackend,
collabora = CellsCollabora Cool,
storage = CellsStorage (NumBytes (BigIntString 2000000000000))
storage =
CellsStorage
{ totalLimitBytes = Just (QuotaBytesFinite (NumBytes (BigIntString 2000000000000))),
perUserQuotaBytes = QuotaBytesUnlimited
}
}
} ::
LockableFeature CellsInternalConfig
Expand Down
Loading