Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Terrarium - Providers]: Implementation of WebUI & REST microservice for providers support #67

Merged
merged 16 commits into from
Mar 19, 2024
Merged
8 changes: 7 additions & 1 deletion cmd/allInOne.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
storage2 "github.com/terrariumcloud/terrarium/internal/module/services/storage"
"github.com/terrariumcloud/terrarium/internal/module/services/tag_manager"
"github.com/terrariumcloud/terrarium/internal/module/services/version_manager"
providers_services "github.com/terrariumcloud/terrarium/internal/provider/services"
"github.com/terrariumcloud/terrarium/internal/release/services/release"
"github.com/terrariumcloud/terrarium/internal/restapi/browse"
modulesv1 "github.com/terrariumcloud/terrarium/internal/restapi/modules/v1"
Expand Down Expand Up @@ -106,9 +107,14 @@ var allInOneCmd = &cobra.Command{
)
startAllInOneGrpcServices([]services2.Service{gatewayServer}, allInOneGrpcGatewayEndpoint)

version_manager_svc, err := providers_services.NewJSONFileProviderVersionManager()
if err != nil {
panic(err)
}

restAPIServer := browse.New(registrar.NewRegistrarGrpcClient(allInOneInternalEndpoint),
version_manager.NewVersionManagerGrpcClient(allInOneInternalEndpoint),
release.NewBrowseGrpcClient(allInOneInternalEndpoint))
release.NewBrowseGrpcClient(allInOneInternalEndpoint), version_manager_svc)

modulesAPIServer := modulesv1.New(version_manager.NewVersionManagerGrpcClient(allInOneInternalEndpoint), storage2.NewStorageGrpcClient(allInOneInternalEndpoint))

Expand Down
8 changes: 7 additions & 1 deletion cmd/browse.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/spf13/cobra"
"github.com/terrariumcloud/terrarium/internal/module/services/registrar"
"github.com/terrariumcloud/terrarium/internal/module/services/version_manager"
providerServices "github.com/terrariumcloud/terrarium/internal/provider/services"
"github.com/terrariumcloud/terrarium/internal/release/services/release"
"github.com/terrariumcloud/terrarium/internal/restapi/browse"
)
Expand All @@ -24,8 +25,13 @@ func init() {

func runBrowseServer(cmd *cobra.Command, args []string) {

version_manager_svc, err := providerServices.NewJSONFileProviderVersionManager()
if err != nil {
panic(err)
}

restAPIServer := browse.New(registrar.NewRegistrarGrpcClient(registrar.RegistrarServiceEndpoint),
version_manager.NewVersionManagerGrpcClient(version_manager.VersionManagerEndpoint),
release.NewBrowseGrpcClient(release.ReleaseServiceEndpoint))
release.NewBrowseGrpcClient(release.ReleaseServiceEndpoint), version_manager_svc)
startRESTAPIService("browse", "", restAPIServer)
}
4 changes: 2 additions & 2 deletions cmd/rest_providers_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ func init() {
}

func runRESTProvidersV1Server(cmd *cobra.Command, args []string) {
vm, err := services.NewJSONFileProviderVersionManager()
version_manager_svc, err := services.NewJSONFileProviderVersionManager()
if err != nil {
panic(err)
}
restAPIServer := providersv1.New(vm)
restAPIServer := providersv1.New(version_manager_svc)
startRESTAPIService("rest-providers-v1", mountPathProviders, restAPIServer)
}
57 changes: 57 additions & 0 deletions internal/provider/services/jsonDataLoader.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import (
"fmt"
"log"
"os"
"strings"

"gopkg.in/errgo.v2/errors"
)

type ProviderVersionManager interface {
ListProviderVersions(providerName string) (*ProviderVersionsResponse, error)
GetVersionData(providerName string, version string, os string, arch string) (*PlatformMetadataResponse, error)
ListProviders() (*ListProviderResponse, error)
GetProviders(providerName string) (*ListProviderItem, error)
}

// Structs to load data into (from a JSON file for now, will be from DB later)
Expand Down Expand Up @@ -80,6 +83,20 @@ type PlatformMetadataResponse struct {
SigningKeys SigningKeys `json:"signing_keys"`
}

// Structs to load response into (for listing providers)

type ListProviderResponse struct {
Providers []*ListProviderItem `json:"providers"`
}

type ListProviderItem struct {
Organization string `json:"organization,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
SourceUrl string `json:"source_url,omitempty"`
Maturity string `json:"maturity,omitempty"`
}

var providerObj map[string]ProviderData

func LoadData() (map[string]ProviderData, error) {
Expand Down Expand Up @@ -178,3 +195,43 @@ func (vm *JSONFileProviderVersionManager) GetVersionData(providerName string, ve
}

}

func (vm *JSONFileProviderVersionManager) ListProviders() (*ListProviderResponse, error) {
var providersList ListProviderResponse

for key := range providerObj {
parts := strings.Split(key, "/")

provider := &ListProviderItem{
Organization: parts[0],
Name: parts[1],
Description: "This is the description for the provider it is supposedly a long text.",
SourceUrl: "https://github.com/...",
Maturity: "3",
}
providersList.Providers = append(providersList.Providers, provider)
}

return &providersList, nil
}

func (vm *JSONFileProviderVersionManager) GetProviders(providerName string) (*ListProviderItem, error) {

if _, exists := providerObj[providerName]; exists {
parts := strings.Split(providerName, "/") //If provider exists, then taking provider name to split the orgName/providerName as taking providerObj is package dependent (struct) & not expected input for Split method (this is for time being)

provider := &ListProviderItem{
Organization: parts[0],
Name: parts[1],
Description: "This is the description for the provider it is supposedly a long text.",
SourceUrl: "https://github.com/...",
Maturity: "DEVELOPMENT",
}
return provider, nil

} else {
errMsg := fmt.Sprintf("failed to retrieve : %s", providerName)
return nil, errors.New(errMsg)
}

}
6 changes: 3 additions & 3 deletions internal/restapi/browse/frontend/build/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files": {
"main.css": "/static/css/main.a593e941.css",
"main.js": "/static/js/main.d65f24d1.js",
"main.js": "/static/js/main.19b86824.js",
"static/js/496.c4cdc242.chunk.js": "/static/js/496.c4cdc242.chunk.js",
"static/media/not-found-1.jpg": "/static/media/not-found-1.3eec1753dacc2df2dcac.jpg",
"static/media/providers.svg": "/static/media/providers.b23747e853582f7fef85d036a949de8f.svg",
Expand All @@ -10,11 +10,11 @@
"static/media/modules.png": "/static/media/modules.df4734ecc28bffc1b755.png",
"index.html": "/index.html",
"main.a593e941.css.map": "/static/css/main.a593e941.css.map",
"main.d65f24d1.js.map": "/static/js/main.d65f24d1.js.map",
"main.19b86824.js.map": "/static/js/main.19b86824.js.map",
"496.c4cdc242.chunk.js.map": "/static/js/496.c4cdc242.chunk.js.map"
},
"entrypoints": [
"static/css/main.a593e941.css",
"static/js/main.d65f24d1.js"
"static/js/main.19b86824.js"
]
}
2 changes: 1 addition & 1 deletion internal/restapi/browse/frontend/build/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Terrarium: Synamedia Terraform Registry</title><script defer="defer" src="/static/js/main.d65f24d1.js"></script><link href="/static/css/main.a593e941.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Terrarium: Synamedia Terraform Registry</title><script defer="defer" src="/static/js/main.19b86824.js"></script><link href="/static/css/main.a593e941.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

32 changes: 32 additions & 0 deletions internal/restapi/browse/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import React from 'react';
import './App.css';
import Main from './pages/main/Main';
import Browse from './pages/modules/browse/Browse';
import BrowseProviders from './pages/providers/browse/Browse'
import Releases from './pages/modules/release/Releases';
import ModuleInfo from './pages/modules/moduleinfo/ModuleInfo';
import ModuleDetailDescription from './pages/modules/moduleinfo/ModuleDetailDescription';
import ModuleDetailVersions from './pages/modules/moduleinfo/ModuleDetailVersions';
import ProviderInfo from './pages/providers/providerinfo/ProviderInfo';
import ProviderDetailDescription from './pages/providers/providerinfo/ProviderDetailDescription';
import ProviderDetailVersions from './pages/providers/providerinfo/ProviderDetailVersions';

import {
createBrowserRouter,
Expand Down Expand Up @@ -65,6 +69,34 @@ const routes = [
},
],
},
{
path: "terraform-providers",
errorElement: <ServerError />,
children: [
{
index: true,
element: <BrowseProviders />,
},
{
path: ":org/:name/",
element: <ProviderInfo />,
children: [
{
index: true,
element: <ProviderDetailDescription />,
},
{
path: "description",
element: <ProviderDetailDescription />,
},
{
path: "versions",
element: <ProviderDetailVersions />,
}
]
},
],
},
// Last route...
{
path: "*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ function Header() {
<Button color="inherit" component={RouterLink} to="/terraform-modules" style={{ textTransform: "capitalize" }}>
Modules
</Button>
<Button color="inherit" component={RouterLink} to="/terraform-providers" style={{ textTransform: "capitalize" }}>
Providers
</Button>
<Button color="inherit" component={RouterLink} to="/releases" style={{ textTransform: "capitalize" }}>
Releases
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export default function SearchBar({filterValue, setFilter}: {filterValue?: strin
</Search>
}
<Box sx={{ flexGrow: 1 }} />
<Button color="inherit" component={RouterLink} to="/terraform-modules">Browse</Button>
<Button color="inherit" component={RouterLink} to="/">Browse</Button>
</Toolbar>
</AppBar>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {useEffect, useState} from 'react';

export interface ProviderResponseData {
providers: ProviderEntry[]
count: number
}

export interface ProviderResponse {
providers: ProviderEntry[]
}

export interface ProviderEntry {
organization: string
name: string
description?: string
source_url: string
maturity?: string
}

export const useProviderList = ():ProviderEntry[] => {
const providerListURI = "/api/providers"
const [providers, setProviders] = useState<ProviderEntry[]>([])
useEffect(() => {
fetch(providerListURI)
.then((response) => {
return response.json();
})
.then((response: ProviderResponse) => {
setProviders(response.providers);
})
}, [])
return providers
}

export const useFilteredProviderList = ():[ProviderEntry[], string, ((value: (((prevState: string) => string) | string)) => void)] => {
const providers = useProviderList()
const [filterText, setFilterText] = useState<string>("")
const filteredProviders = providers
.filter((providerInfo) => {
const filterValue = filterText.toLowerCase()

if (filterText === "") {
return true
}
const providerSearchText = providerInfo.organization + " "
+ providerInfo.name

return providerSearchText.toLowerCase().includes(filterValue)
})
return [filteredProviders, filterText, setFilterText]
}
29 changes: 29 additions & 0 deletions internal/restapi/browse/frontend/src/data/useProviderMetadata.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {useEffect, useState} from 'react';

export interface ProviderMetadata {
organization: string
name: string
description?: string
source_url: string
maturity?: string
versions: string[]
}

interface ProviderMetadataResponse {
data: ProviderMetadata
}

export const useProviderMetadata = (organization: string | undefined, name: string | undefined) => {
const [providerMetadata, setProviderMetadata] = useState<ProviderMetadata|null>(null)
const providerMetadataURI = `/api/providers/${organization}/${name}`
useEffect(() => {
fetch(providerMetadataURI)
.then((response) => {
return response.json();
})
.then((response: ProviderMetadataResponse) => {
setProviderMetadata(response.data);
})
}, [providerMetadataURI])
return providerMetadata
}
16 changes: 14 additions & 2 deletions internal/restapi/browse/frontend/src/pages/main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,21 @@ function Main() {
</Grid>
<Grid item xs={7} >
<Typography variant="h5" align='right' gutterBottom>Providers</Typography>
<Typography variant="body2" align='right' gutterBottom>
With Terrarium you will be able to publish and consume Terraform Provider. But it is not yet available.
<Typography variant="body2" gutterBottom>
With Terrarium you will be able to publish and consume Terraform Provider. All the provider that are
currently available are available for consumption to include a provider you just need a few things:
</Typography>
<List>
<ListItem>You need to declare the provider in your terraform code.</ListItem>
<ListItem>You need to reference the terrarium address for the provider.</ListItem>
<ListItem>You need to specify the version of the provider.</ListItem>
</List>
<Typography variant="body2" gutterBottom>
To discover the list of available providers and their versions, Terrarium comes with provider discovery
and search capabilities which we hopefully made friendly enough to be of use.
</Typography>
<br />
<Button variant="contained" fullWidth component={RouterLink} to="terraform-providers">Browse for providers</Button>
</Grid>
</Grid>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import SearchBar from '../../../components/search-bar/SearchBar';
import { Button, Card, CardContent, CardActions, Stack, Typography, Paper } from '@mui/material';
import { useFilteredProviderList, ProviderEntry } from '../../../data/useFilteredProviderList'
import { Link as RouterLink } from 'react-router-dom';

function BrowseProviders() {
const [filteredProviderList, filterText, setFilterText] = useFilteredProviderList();

const ProviderCard = ({ provider }: { provider: ProviderEntry }) => {
const providerPage: string = `${provider.organization}/${provider.name}`
return (
<Card >
<CardContent>
<Typography color="text.primary">{provider.organization || "Synamedia"} / {provider.name}</Typography>
<Typography variant="body2">{provider.description || "A provider"}</Typography>
</CardContent>
<CardActions>
<Button size="small" href={provider.source_url || ""}>Source</Button>
<Button size="small" component={RouterLink} to={providerPage}>Provider Info</Button>
</CardActions>
</Card>
)
}

return (
<>
<SearchBar filterValue={filterText} setFilter={setFilterText} />
<Stack spacing={2} style={{ marginTop: ".8em", marginBottom: ".8em" }}>
<Paper>
<Typography variant="h5">Matching providers: {filteredProviderList.length}</Typography>
</Paper>
{filteredProviderList.map((mod, index) => { return <ProviderCard provider={mod} key={index} /> })}
</Stack>
</>
);
}

export default BrowseProviders;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

const ProviderDetailDescription = () => {
return (
<p>DescriptionTab</p>
)
}

export default ProviderDetailDescription
Loading