<?php
namespace App\DataProvider;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryResultCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use ApiPlatform\Core\Exception\RuntimeException;
use App\Entity\Company;
use Doctrine\Common\Persistence\ManagerRegistry;
class NetworkCollectionDataProvider implements CollectionDataProviderInterface
{
private $managerRegistry;
private $collectionExtensions;
/**
* @param ManagerRegistry $managerRegistry
* @param QueryCollectionExtensionInterface[] $collectionExtensions
*/
public function __construct(ManagerRegistry $managerRegistry, array $collectionExtensions = [])
{
$this->managerRegistry = $managerRegistry;
$this->collectionExtensions = $collectionExtensions;
}
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return Company::class === $resourceClass;
}
/**
* {@inheritdoc}
*
* @throws RuntimeException
*/
public function getCollection(string $resourceClass, string $operationName = null): Paginator
{
$manager = $this->managerRegistry->getManagerForClass($resourceClass);
if (null === $manager) {
throw new ResourceClassNotSupportedException();
}
$repository = $manager->getRepository($resourceClass);
if (!method_exists($repository, 'createQueryBuilder')) {
throw new RuntimeException('The repository class must have a "createQueryBuilder" method.');
}
$queryBuilder = $repository->findNetworkArray();
$queryNameGenerator = new QueryNameGenerator();
foreach ($this->collectionExtensions as $extension) {
$extension->applyToCollection($queryBuilder, $queryNameGenerator, $resourceClass, $operationName);
if ($extension instanceof QueryResultCollectionExtensionInterface && $extension->supportsResult($resourceClass, $operationName)) {
return $extension->getResult($queryBuilder);
}
}
return $queryBuilder->getQuery()->getResult();
}
}
CompanyController
<?php
namespace App\Controller;
use App\Entity\Company;
use App\Repository\CompanyRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
use Symfony\Component\HttpFoundation\Request;
use App\DataProvider\NetworkCollectionDataProvider;
class CompanyController extends AbstractController
{
private $networkCollectionDataProvider;
public function __construct(NetworkCollectionDataProvider $networkCollectionDataProvider)
{
$this->networkCollectionDataProvider = $networkCollectionDataProvider;
}
/**
* @Route(
* name="network",
* path="v1/network",
* methods={"GET"},
* defaults={
* "_api_resource_class"="App\Entity\Company",
* "_api_collection_operation_name"="network"
* }
* )
*/
public function __invoke()
{
return $this->networkCollectionDataProvider->getCollection(Company::class);
}
}
services
App\DataProvider\NetworkCollectionDataProvider:
tags: [ { name: 'api_platform.collection_data_provider', priority: 2 } ]
# Autoconfiguration must be disabled to set a custom priority
autoconfigure: false
# Mojo Blog
Mojo Blog is simple example with [DBD::Pg](https://metacpan.org/pod/DBD::Pg) that makes [PostgreSQL](https://www.postgresql.org) a lot of fun to use with the [Mojolicious](https://mojolicious.org) real-time web framework.
## Local installation
### Perl
You can use system Perl version for development. I recommend the use of `perlbrew` or `plenv` environment.
- [perlbrew](https://perlbrew.pl/)
- [plenv](https://github.com/tokuhirom/plenv/)
### Prerequisites
Cpanminus and Carton are the only prerequisite for running the application. All required modules/dependencies are then installed via Carton from cpanfile.
- [App::cpanminus - get, unpack, build and install modules from CPAN](https://github.com/miyagawa/cpanminus/tree/devel/App-cpanminus)
- [Carton - Perl module dependency manager (aka Bundler for Perl)](https://metacpan.org/pod/Carton)
### Configuration
For Perl-ish configuration Mojolicious plugin is used - [Mojolicious::Plugin::Config](http://mojolicious.org/perldoc/Mojolicious/Plugin/Config).
## Building the Docker container image
### Building the latest image
```bash
$ docker build -t <NAME:TAG> .
```
### Running tests inside container image
```bash
$ docker run --rm -v $PWD/t:/opt/app/t <IMAGE> carton exec prove -lr -j4
```
## Working with Helm
### Install `tiller` in the Minikube cluster
```bash
$ helm init
```
### We can do a dry-run of a helm install and enable debug to inspect the generated definitions:
```bash
$ helm install --dry-run --debug --namespace=default ./helm
```
### Install
```bash
$ helm install --name mojo-blog --namespace=default ./helm
```
### Check the status of the release
```bash
$ helm ls --all mojo-blog
```
### Delete the release
```bash
$ helm del --purge mojo-blog
```
### Do the linting
```bash
$ helm lint ./helm
```
### Render chart templates locally and store them as Kubernetes YAML
```bash
$ helm template --name mojo-blog --namespace=default ./helm > mojo-blog.yaml
```
## Use Port Forwarding to Access Applications in a Cluster
### Forward a local port to a port on the pod / deployment / service
```bash
$ kubectl port-forward <POD> 8080:8080
# or
$ kubectl port-forward dc/<DEPLOYMENT> 8080:8080
# or
$ kubectl port-forward svc/<SERVICE> 8080:8080
```
ANALYSIS.md
# Analysis
## Handling sensitive data
Currently sensitive information, such as passwords, server IP addresses, ... are hard-coded and stored in plain text. If sensitive data must be saved, it must be encrypted first. The best option would be to use a tool for securely managing secrets and encrypting, for example, HashiCorp Vault.
## Automation
Artefact building and method of deployment are done manually as described in [README.md](README.md) file. The whole process (configuration, software provisioning and application deployment) can be fully automated. To solve the problem of environment drifting in release pipeline Infrastructure as Code (IaC) approach should be used. Continuous configuration automation (CCA) tools can be thought of as an extension of traditional IaC frameworks. Notable CCA tools: Ansible, SaltStack, Terraform.
## Database
For simplicity, PostgreSQL database service is currently deployed without persistent storage (Ephemeral) and, therefore, is not production ready. Any data stored will be lost upon pod destruction. Data persistence on Kubernetes is achieved with Persistent Volumes which provide a plugin model for storage in Kubernetes where how storage is provided is completed abstracted from how it is consumed. For production, PostgreSQL replicated database service with persistent storage (highly available with automated failover and backup) should be used.
## Pipeline
Current Gitlab CI/CD and Jenkins pipeline streamline the very simplified process of build, tag and release for only one environment - TEST. The production TEST stages should include
- Test and Analysis (running unit tests and code quality analysis for using SonarQube or equivalent)
- Deploy (As soon as the latest Docker image is built and pushed to Docker registry a new deployment based on this most recent image is rolled out on the Test environment. This stage will wait until the deployment is fully rolled out and ready.)
- (Optional) Smoke Tests and Module Integration Test (For some implementations it might be useful to implement smoke test and module integration tests. These tests can be executed after the successful rollout of a new deployment.)
- Tag Docker Image (After the successful rollout (and optional smoke and module integration tests), the image is tagged with the current build version.)
- Create GIT Release Tag (Finally, the current GIT commit is tagged with the current image build version.)
The next steps can include transporting immutable images across the various stages (INT, PROD) or even across the different clusters.
## Software Versioning
Versioning is essential in application development. It must be possible to relate every deployment to one unique SCM commit to:
- Verify that the correct version with all its desired features is deployed
- Reproduce errors or problems by analysing the code of the exact SCM commit
- Rolling out one specific (stable) version of the application to other environments
To keep it clear and straightforward semantic versioning is used which is also used for versioning the Libraries in software development. For Continuous microservices release cycles, on the other hand, rely heavily on these time-based parameters due to their much shorter lifetime and could almost renounce semantic versioning entirely. Though it is recommended to keep a semantic version for microservices to indicate major and minor releases and track microservice development from a top view.
Example: 20170717.111749-master-1.2.3
Additional tagging should also be applied after successful rollouts to INT / PROD in GIT.
## Hypnotoad Prefork Web Server
Mojo::Server::Hypnotoad is a built-in prefork web server in production environment. hypnotoad is command line interface to run Mojolicious application. Server start on port 8080 by default which is also port exposed in Docker image. In production generally reverse proxy server (Nginx, Apache) is used to access hypnotoad server.