From 092d2ee39fb79d3b8ea4ff38eda758e24a19584f Mon Sep 17 00:00:00 2001 From: Hiram Chirino Date: Fri, 15 Dec 2023 12:04:40 -0500 Subject: [PATCH] Add feature flags for site and device apis Signed-off-by: Hiram Chirino --- deploy/nexodus/base/apiserver/deployment.yaml | 10 +++++ .../nexodus/base/apiserver/kustomization.yaml | 2 + .../overlays/playground/kustomization.yaml | 13 +++--- internal/handlers/api.go | 12 ++++- ui/src/App.tsx | 45 +++---------------- ui/src/DataProvider.tsx | 40 +++++++++++++++++ ui/src/layout/Menus.tsx | 41 ++++++++++++----- 7 files changed, 106 insertions(+), 57 deletions(-) create mode 100644 ui/src/DataProvider.tsx diff --git a/deploy/nexodus/base/apiserver/deployment.yaml b/deploy/nexodus/base/apiserver/deployment.yaml index caafd8110..d0f5c6e65 100644 --- a/deploy/nexodus/base/apiserver/deployment.yaml +++ b/deploy/nexodus/base/apiserver/deployment.yaml @@ -205,6 +205,16 @@ spec: name: nexodus-ca-key-pair key: tls.key optional: true + - name: NEXAPI_FFLAG_DEVICES + valueFrom: + configMapKeyRef: + name: apiserver + key: NEXAPI_FFLAG_DEVICES + - name: NEXAPI_FFLAG_SITES + valueFrom: + configMapKeyRef: + name: apiserver + key: NEXAPI_FFLAG_SITES # CI deployment seems to fail when this is enabled # readinessProbe: diff --git a/deploy/nexodus/base/apiserver/kustomization.yaml b/deploy/nexodus/base/apiserver/kustomization.yaml index 6a05c4783..48ce43606 100644 --- a/deploy/nexodus/base/apiserver/kustomization.yaml +++ b/deploy/nexodus/base/apiserver/kustomization.yaml @@ -23,6 +23,8 @@ configMapGenerator: - NEXAPI_FETCH_MGR=redis - NEXAPI_FETCH_MGR_TIMEOUT=2s - NEXAPI_DEVICE_CACHE_SIZE=500 + - NEXAPI_FFLAG_DEVICES=true + - NEXAPI_FFLAG_SITES=false resources: - service.yaml - deployment.yaml diff --git a/deploy/nexodus/overlays/playground/kustomization.yaml b/deploy/nexodus/overlays/playground/kustomization.yaml index 03eab6443..4b1041d8b 100644 --- a/deploy/nexodus/overlays/playground/kustomization.yaml +++ b/deploy/nexodus/overlays/playground/kustomization.yaml @@ -6,15 +6,16 @@ resources: namespace: nexodus-playground configMapGenerator: - behavior: replace + name: auth-config literals: - hostname=auth.playground.nexodus.io - frontend-url=https://playground.nexodus.io - name: auth-config - behavior: replace + name: realm files: - files/nexodus.json - name: realm - behavior: merge + name: apiproxy literals: - APIPROXY_API_URL=https://api.playground.nexodus.io - APIPROXY_OIDC_URL=https://auth.playground.nexodus.io/realms/nexodus @@ -22,8 +23,8 @@ configMapGenerator: - APIPROXY_WEB_DOMAIN=playground.nexodus.io - APIPROXY_WEB_ORIGINS=https://playground.nexodus.io - ENVOY_COMP_LOG_LEVEL=upstream:info,http:info,router:info,jwt:info - name: apiproxy - behavior: merge + name: apiserver literals: - NEXAPI_URL=https://api.playground.nexodus.io - NEXAPI_OIDC_URL=https://auth.playground.nexodus.io/realms/nexodus @@ -31,9 +32,11 @@ configMapGenerator: - NEXAPI_REDIRECT_URL=https://playground.nexodus.io/#/login - NEXAPI_ORIGINS=https://playground.nexodus.io - NEXAPI_ENVIRONMENT=qa - - NEXAPI_FFLAG_SECURITY_GROUPS=true + - NEXAPI_FFLAG_SITES=true + - NEXAPI_FFLAG_DEVICES=false + - NEXAPI_FFLAG_DEVICES=true + - NEXAPI_FFLAG_SITES=false - NEXAPI_DEBUG=0 - name: apiserver patches: # Update the dns names for the certificates diff --git a/internal/handlers/api.go b/internal/handlers/api.go index a114398ce..0ef7116b3 100644 --- a/internal/handlers/api.go +++ b/internal/handlers/api.go @@ -14,6 +14,7 @@ import ( "github.com/nexodus-io/nexodus/internal/signalbus" "github.com/redis/go-redis/v9" "net/http" + "os" "strconv" "github.com/nexodus-io/nexodus/internal/database" @@ -73,8 +74,17 @@ func NewAPI( fflags.RegisterEnvFlag("multi-organization", "NEXAPI_FFLAG_MULTI_ORGANIZATION", true) fflags.RegisterEnvFlag("security-groups", "NEXAPI_FFLAG_SECURITY_GROUPS", true) + fflags.RegisterEnvFlag("devices-api", "NEXAPI_FFLAG_DEVICES", true) + fflags.RegisterEnvFlag("sites-api", "NEXAPI_FFLAG_SITES", false) fflags.RegisterFlag("ca-api", func() bool { - return caKeyPair.Certificate != nil + sitesFlag, err := strconv.ParseBool(os.Getenv("NEXAPI_FFLAG_SITES")) + if err != nil || !sitesFlag { + return false + } + if caKeyPair.Certificate != nil { + return false + } + return true }) ctx, span := tracer.Start(parent, "NewAPI") diff --git a/ui/src/App.tsx b/ui/src/App.tsx index f67ebdf6c..68e872003 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,9 +1,6 @@ -import { Admin, CustomRoutes, Resource, fetchUtils } from "react-admin"; +import { Admin, CustomRoutes, Resource } from "react-admin"; import { Route } from "react-router"; -import simpleRestProvider from "ra-data-simple-rest"; -import { goOidcAgentAuthProvider } from "./providers/AuthProvider"; - // icons import DeviceIcon from "@mui/icons-material/Devices"; import SiteIcon from "@mui/icons-material/BorderOuter"; @@ -14,14 +11,14 @@ import RegKeyIcon from "@mui/icons-material/Key"; import VPCIcon from "@mui/icons-material/Cloud"; // pages -import { UserShow, UserList } from "./pages/Users"; +import { UserList, UserShow } from "./pages/Users"; import { DeviceEdit, DeviceList, DeviceShow } from "./pages/Devices"; import { + OrganizationCreate, OrganizationList, OrganizationShow, - OrganizationCreate, } from "./pages/Organizations"; -import { VPCList, VPCShow, VPCCreate } from "./pages/VPCs"; +import { VPCCreate, VPCList, VPCShow } from "./pages/VPCs"; import Dashboard from "./pages/Dashboard"; import LoginPage from "./pages/Login"; import Layout from "./layout/Layout"; @@ -41,39 +38,7 @@ import { RegKeyShow, } from "./pages/RegKeys"; import { SiteEdit, SiteList, SiteShow } from "./pages/Sites"; - -const fetchJson = (url: string, options: any = {}) => { - // Includes the encrypted session cookie in requests to the API - options.credentials = "include"; - // some of the PUT api calls should be converted to PATCH - if (options.method === "PUT") { - if ( - url.startsWith(`${backend}/api/reg-keys/`) || - url.startsWith(`${backend}/api/devices/`) || - url.startsWith(`${backend}/api/security-groups/`) || - url.startsWith(`${backend}/api/vpcs/`) - ) { - options.method = "PATCH"; - } - } - return fetchUtils.fetchJson(url, options); -}; - -const backend = `${window.location.protocol}//api.${window.location.host}`; -const authProvider = goOidcAgentAuthProvider(backend); -const baseDataProvider = simpleRestProvider( - `${backend}/api`, - fetchJson, - "X-Total-Count", -); -const dataProvider = { - ...baseDataProvider, - getFlag: (name: string) => { - return fetchJson(`${backend}/api/fflags/${name}`).then( - (response) => response, - ); - }, -}; +import { authProvider, dataProvider } from "./DataProvider"; const App = () => { return ( diff --git a/ui/src/DataProvider.tsx b/ui/src/DataProvider.tsx new file mode 100644 index 000000000..ddbb5475a --- /dev/null +++ b/ui/src/DataProvider.tsx @@ -0,0 +1,40 @@ +import { goOidcAgentAuthProvider } from "./providers/AuthProvider"; +import simpleRestProvider from "ra-data-simple-rest"; +import { fetchUtils } from "react-admin"; + +const fetchJson = (url: string, options: any = {}) => { + // Includes the encrypted session cookie in requests to the API + options.credentials = "include"; + // some of the PUT api calls should be converted to PATCH + if (options.method === "PUT") { + if ( + url.startsWith(`${backend}/api/reg-keys/`) || + url.startsWith(`${backend}/api/devices/`) || + url.startsWith(`${backend}/api/security-groups/`) || + url.startsWith(`${backend}/api/vpcs/`) + ) { + options.method = "PATCH"; + } + } + return fetchUtils.fetchJson(url, options); +}; +const backend = `${window.location.protocol}//api.${window.location.host}`; +export const authProvider = goOidcAgentAuthProvider(backend); +const baseDataProvider = simpleRestProvider( + `${backend}/api`, + fetchJson, + "X-Total-Count", +); +export const dataProvider = { + ...baseDataProvider, + getFlag: (name: string) => { + return fetchJson(`${backend}/api/fflags/${name}`).then( + (response) => response, + ); + }, + getFlags: async () => { + return (await fetchJson(`${backend}/api/fflags`)).json as { + [index: string]: boolean; + }; + }, +}; diff --git a/ui/src/layout/Menus.tsx b/ui/src/layout/Menus.tsx index 2663e3914..d711125d5 100644 --- a/ui/src/layout/Menus.tsx +++ b/ui/src/layout/Menus.tsx @@ -7,8 +7,21 @@ import InvitationIcon from "@mui/icons-material/Rsvp"; import { MenuProps } from "react-admin"; import RegKeyIcon from "@mui/icons-material/Key"; import VPCIcon from "@mui/icons-material/Cloud"; +import { dataProvider } from "../DataProvider"; +import { useEffect, useState } from "react"; export const CustomMenu = (props: MenuProps) => { + const [flags, setFlags] = useState({} as { [index: string]: boolean }); + useEffect(() => { + (async () => { + try { + setFlags(await dataProvider.getFlags()); + } catch (e) { + console.log(e); + } + })(); + }, []); + return ( @@ -18,22 +31,28 @@ export const CustomMenu = (props: MenuProps) => { leftIcon={} /> } /> - } - /> - } /> + {flags["devices-api"] && ( + } + /> + )} + {flags["sites-api"] && ( + } /> + )} } /> - } - /> + {flags["devices-api"] && ( + } + /> + )}