text:
[//000000001]: # (canvas::edit::quadrilateral \- Variations on a canvas)
[//000000002]: # (Generated from file 'canvas\_equad\.man' by tcllib/doctools with format 'markdown')
[//000000003]: # (canvas::edit::quadrilateral\(n\) 0\.1 tklib "Variations on a canvas")

 [ Main Table Of Contents | Table Of Contents | Keyword Index | Categories | Modules | Applications ]

# NAME

canvas::edit::quadrilateral \- Editing a quadrilateral on a canvas

# Table Of Contents

  - [Table Of Contents](#toc)

  - [Synopsis](#synopsis)

  - [Description](#section1)

  - [Class API](#section2)

  - [Instance API](#section3)

  - [Options](#section4)

  - [Bugs, Ideas, Feedback](#section5)

  - [Keywords](#keywords)

# SYNOPSIS

package require Tcl 8\.5
package require Tk 8\.5
package require canvas::edit::points ?0\.1?
package require canvas::edit::quadrilateral ?0\.1?

[__::canvas::edit__ __quadrilateral__ *objectName* *canvas* *options*\.\.\.](#1)
[__objectName__ __destroy__](#2)
[__objectName__ __enable__](#3)
[__objectName__ __disable__](#4)
[__objectName__ __active__](#5)
[__objectName__ __add__ *x* *y*](#6)
[__objectName__ __clear__](#7)
[__createCmd__ *canvas* *x* *y*](#8)
[__highlightCmd__ __on__ *canvas* *item*](#9)
[__highlightCmd__ __off__ *canvas* *state*](#10)
[__dataCmd__ *editorObj* *coordinates*](#11)

# DESCRIPTION

This package provides a class whose instances handle the editing of a
quadrilateral on a canvas\. Instances can be configured with regard to the visual
appearance \(regular, and highlighted\) of vertex markers\. Note that instances do
not store the edited quadrilateral themselves, but delegate this to a
configurable object\.

# Class API

  - __::canvas::edit__ __quadrilateral__ *objectName* *canvas* *options*\.\.\.

    This, the class command, creates and configures a new instance of a
    quadrilateral editor, named *objectName*\. The instance will be connected
    to the specified *canvas* widget\.

    The result of the command is the fully qualified name of the instance
    command\.

    The options accepted here, and their values, are explained in the section
    [Options](#section4)\.

# Instance API

Instances of the quadrilateral editors provide the following API:

  - __objectName__ __destroy__

    This method destroys the quadrilateral editor and releases all its internal
    resources\.

    Note that this operation does not destroy the items the editor managed on
    the attached canvas, nor the canvas itself\.

    The result of the method is an empty string\.

  - __objectName__ __enable__

    This method activates editing of the quadrilateral on the canvas\. This is
    the default after instance creation\. A call is ignored if the editor is
    already active\.

    The result of the method is an empty string\.

    The complementary method is __disable__\. The interogatory method for the
    current state is __active__\.

  - __objectName__ __disable__

    This method disables editing of the quadrilateral on the canvas\. A call is
    ignored if the editor is already disabled\.

    The result of the method is an empty string\.

    The complementary method is __enable__\. The interogatory method for the
    current state is __active__\.

  - __objectName__ __active__

    This method queries the editor state\.

    The result of the method is a boolean value, __true__ if the editor is
    active, and __false__ otherwise, i\.e\. disabled\.

    The methods to change the state are __enable__ and __disable__\.

  - __objectName__ __add__ *x* *y*

    This method programmatically adds a vertex at the specified location to the
    quadrilateral\.

    The result of the method is an empty string\.

    Note that this method goes through the whole set of callbacks invoked when
    the user interactively creates a vertex, i\.e\. __\-create\-cmd__, and, more
    importantly, __\-data\-cmd__\.

    This is the method through which to load the vertices of a pre\-existing
    quadrilateral into an editor instance\.

    Note that at most 4 vertices can be specified, and at least 4 must be
    specified for the quadrilateral to be complete\.

  - __objectName__ __clear__

    This method programmatically unset the quadrilateral in the editor\.

    The result of the method is an empty string\.

    Note that this method goes through the same callback invoked when the user
    interactively removes a vertex, i\.e\. __\-data\-cmd__\.

# Options

The class command accepts the following options

  - __\-convex__ *boolean*

    The value of this option is a boolean flag\. When the flag is set the editor
    will accept only convex quadrilaterals and reject all operations which would
    result in a violation of convexity\.

    This option can only be set at construction time\.

    If not specified it defaults to __false__, i\.e\. acceptance of any
    quadrilateral\.

  - __\-tag__ *string*

    The value of this option is the name of the canvas tag with which to
    identify all items of all quadrilateral managed by the editor\.

    This option can only be set at construction time\.

    If not specified it defaults to __QUADRILATERAL__

  - __\-create\-cmd__ *command\-prefix*

    The value of this option is a command prefix the editor will invoke when it
    has to create a new vertex\.

    This option can only be set at construction time\.

    If not specified it defaults to a command which will create a black\-bordered
    blue circle of radius 3 centered on the location of the new point\.

    The signature of this command prefix is

      * __createCmd__ *canvas* *x* *y*

        The result of the command prefix *must* be a list of the canvas items
        it created to represent the marker\. Note here that the visual
        representation of a "vertex" may consist of multiple canvas items in an
        arbitrary shape\.

  - __\-highlight\-cmd__ *command\-prefix*

    The value of this option is a command prefix the editor will invoke when it
    has to \(un\)highlight a vertex\.

    This option can only be set at construction time\.

    If not specified it defaults to a command which will re\-color the item to
    highlight in red \(and restores the color for unhighlighting\)\.

    The two signatures of this command prefix are

      * __highlightCmd__ __on__ *canvas* *item*

        This method of the command prefix has to perform whatever is needed to
        highlight the vertex the *item* is a part of \(remember the note above
        about quadrilateral allowed to be constructed from multiple canvas
        items\)\.

        The result of the command can be anything and will be passed as is as
        argument *state* to the __off__ method\.

      * __highlightCmd__ __off__ *canvas* *state*

        This method is invoked to unhighlight a vertex described by the
        *state*, which is the unchanged result of the __on__ method of the
        command prefix\. The result of this method is ignored\.

        Note any interaction between dragging and highlighting of quadrilateral
        is handled within the editor, and that the callback bears no
        responsibility for doing such\.

  - __\-data\-cmd__ *command\-prefix*

    The value of this option is a command prefix the editor will invoke when the
    quadrilateral was edited in some way\. This is how the editor delegates the
    actual storage of quadrilateral information to an outside object\.

    This option can only be set at construction time\.

    If not specified it defaults to an empty string and is ignored by the
    editor, i\.e\. not invoked\.

    The signatures of this command prefix are

      * __dataCmd__ *editorObj* *coordinates*

        This callback is invoked when the quarilateral was changed either
        interactively, or programmatically\. See instance method __add__ for
        the latter\.

        The *editorObj* identifies the instance invoking the callback, whereas
        *coordinates* is a list of vertex locations, with each location a pair
        of x\- and y\-coordinates\.

        The result of this method is ignored\.

# Bugs, Ideas, Feedback

This document, and the package it describes, will undoubtedly contain bugs and
other problems\. Please report such in the category *canvas* of the [Tklib
Trackers](http://core\.tcl\.tk/tklib/reportlist)\. Please also report any ideas
for enhancements you may have for either package and/or documentation\.

# KEYWORDS

[canvas](\.\./\.\./\.\./\.\./index\.md\#canvas),
[concave](\.\./\.\./\.\./\.\./index\.md\#concave),
[convex](\.\./\.\./\.\./\.\./index\.md\#convex),
[editing](\.\./\.\./\.\./\.\./index\.md\#editing),
[non\-convex](\.\./\.\./\.\./\.\./index\.md\#non\_convex),
[quadrilateral](\.\./\.\./\.\./\.\./index\.md\#quadrilateral)

repository: tcltk/tklib

gha_language: Tcl

repo_name: tcltk/tklib


text:
[![Cookbook Version](https://img.shields.io/cookbook/v/cpu.svg)](https://supermarket.getchef.com/cookbooks/cpu) [![Build Status](http://img.shields.io/travis/Youscribe/cpu-cookbook/master.svg)](https://travis-ci.org/Youscribe/cpu-cookbook)

CPU Cookbook
============
This cookbook is used to configure a system's CPU affinity and governor.

# Description

Manage CPU Governor on linux

Requirements
------------
* Should work on Chef 10 and above on supported Linux distributions

## Platform:

* Ubuntu
* Debian
* SUSE
* RHEL
* CentOS

## Cookbooks:

*No dependencies defined*

# Attributes
* governor - Defaults to 'ondemand'
* utilities['affinity', 'governor'] - Platform-specific packages for controlling processor affinity and governor
* enable - Array of utilities to enable, defaults to ['governor']
* install_method - Package installation method, defaults to 'install'

# Recipes

* cpu::default - In most cases calling this directly will be sufficient. The enable array will determine which additional recipes from the below list are included.
* cpu::affinity
* cpu::governor

# Resources

* [cpu_affinity](#cpu_affinity)
* [cpu_nice](#cpu_nice)

## cpu_affinity

### Actions

- set:  Default action.

### Attribute Parameters

- pid:
- cpu:

### Examples

```
cpu_affinity 1234 do
  cpu 0
end
```

Set affinity to processor 0,1,2 for process nginx
```
cpu-affinity 'set affinity for nginx' do
  pid '/var/run/nginx.pid'
  cpu '0-2'
end
```

## cpu_nice

### Actions

- set:  Default action.

### Attribute Parameters

- pid:
- priority:

### Examples

```
cpu_nice 1234 do
  priority 12
end
```

```
cpu_nice 'set affinity for nginx' do
  pid '/var/run/nginx.pid'
  priority 19
end
```

# License and Maintainer

Maintainer:: Jonathan Bogaty<EMAIL_ADDRESS>
License:: Apache v2.0

repository: jbcom/cpu-cookbook

branch_name: refs/heads/master

extension: md

license_type: permissive

repo_name: jbcom/cpu-cookbook


text:
---
title: Azure Event Grid Trigger Logic App
date: 2020-04-26T08:06:24-05:00
author: gerryw1389
layout: single
classes: wide
permalink: /2020/04/azure-event-grid-trigger-logic-app
categories:
  - Azure
tags:
  - Cloud
  - Orchestration
  - Azure-LogicApps
  - Azure-EventGrid
  - Scripting-RestAPI
---
<!--more-->

### Description:

Having a runbook [run every minute](https://automationadmin.com//2020/04/azure-function-app-run-every-minute) in one way to automate things. But it just feels wrong. Why not make things event based instead? Well this is my first attempt at using Azure Event Grid to do just that.

### To Resolve:

1. Create a new Event Grid Domain:
   - Go to Event Grid Domains and create a new one `MyTestEG`
   - Note the endpoint it creates - `https://mytesteg.southcentralus-1.eventgrid.azure.net/api/events`

2. Create a new topic:
   - Go to Event Grid Topics and create a new one `MyTestTopic`
   - Add a new Event Subscription: `MyTestSubscription`
   - Have the subscription call a logic app: Paste in URL for HTTP webook
     - If you haven't already, create a logic app
     - Set its trigger to be `When a HTTP Request is Received`
     - Request body json: Paste in body (from next step): It generates json schema for you.
     - Now add a step to 'Send An Email (v2)' - have it send you an email so you can see when successful
     - Hit save
     - After it saves the first time, copy its `HTTP POST URL`

3. Send test payload:

   - First, go back to Event Grid Domains => MyTestEG => Access Keys => Copy the first key => Put in Keepass (optional)
   - It will be used in the header under `aeg-sas-key`

   - Headers:

   ```escape
   POST https://mytesteg.southcentralus-1.eventgrid.azure.net/api/events
   Content-Type: application/json
   aeg-sas-key: seekeepass
   Content-Type: text/plain
   ```

   - Body:

   ```escape
   [
   {
      "id": "REQ12345678",
      "subject": "someRequest",
      "topic": "myTestTopic",
      "eventType": "Service Now",
      "eventTime": "2019-02-13T18:41:00.9584103Z",
      "data": {
         "otherdata": "something"
         },
      "dataVersion": "1.0",
      "metadataVersion": null
   }
   ]
   ```

4. Did this trigger your logic app which resulted in an email to you (per step 2)? This may not work because I have removed my domain specific stuff and I know we got it working but it may not work since it's been sanitized - let me know in the comments.

5. So this is good and all, but we need a way to filter events in case we get more than a few of these. You can do this with `Subject filtering`:

   - Go back to Event Topics. Click `MyTestTopic` and `+ Event Subscription`
   - Name = TESTLogicApp
   - Event Schema: Event Grid Schema
   - Endpoint Type: Web Hook
   - Endpoint: (webhook from above)
   - Topic Type: Event Grid Domain
   - Topic Resource: MyTestEG
   - Domain Topic: MyTestTopic
   - Generate random guid from [https://guidgenerator.com/online-guid-generator.aspx](https://guidgenerator.com/online-guid-generator.aspx)
   - Filter tab = Check the box to enable subject filtering and choose 'subject begins with' and paste in '6a99c7e2-5dab-4036-b1fc-2a74b65967ef'
   - Save / Exit

6. Test from your machine using Postman
   - Headers: Same
   - Body:

   ```escape
   [
      {
         "id": "REQ12345678",
         "subject": "s6a99c7e2-5dab-4036-b1fc-2a74b65967ef",
         "topic": "myTestTopic",
         "eventType": "Service Now",
         "eventTime": "2019-02-13T18:41:00.9584103Z",
         "data": {
            "otherdata": "something"
            },
         "dataVersion": "1.0",
         "metadataVersion": null
      }
   ]
   ```

license: MIT

branch_name: refs/heads/main


text:
# Skel

All my bash skeleton files.

### bash_aliases

A collection of bash functions, simple shell completion, and aliases to make
life just a little bit easier.

### bash_completion.maven

Bash completion for maven is provided by
<https://github.com/juven/maven-bash-completion>

### bash_logout

Simply clears the console on logout for privacy.

### bash_profile

Just sources `~/.bashrc` and iteratively appends directories to the `$PATH`
environmental variable if the directory exists.

### bashrc

My `~/.bashrc` file. Sources the system `/etc/bashrc`,
`~/.bash_completion.maven`, `~/.bash_aliases`, and other shell files.  Sets up
environment variables.

### inputrc

Just some inputrc...

repository: Stanswers/jskel

repo_name: Stanswers/jskel


text:
---

copyright:
  years: 2015, 2017

lastupdated: "2017-05-23"

---

{:shortdesc: .shortdesc}
{:new_window: target="_blank"}
{:codeblock: .codeblock}
{:screen: .screen}

# Logging for the IBM Bluemix Container Service
{: #logging_containers_ov}

You can view, filter, and analyze logs for Docker containers that are deployed in the {{site.data.keyword.IBM}}-managed cloud infrastructure and for Docker containers that run in Kubernetes clusters. Logging of containers is automatically enabled when you deploy a container in {{site.data.keyword.Bluemix_notm}} or in a Kubernetes cluster.
{:shortdesc}

Container logs are monitored and forwarded from outside of the container by using crawlers. The data is sent by the crawlers to a
multi-tenant Elasticsearch in {{site.data.keyword.Bluemix_notm}}.

## Collecting logs for a container that runs in a Kubernetes cluster
{: #logging_containers_ov_logs_collected_kubernetes}

The following figure shows a high level view of logging for the {{site.data.keyword.containershort}}:

![High level component overview for containers deployed in a Kubernetes cluster](images/containers_kube.gif "High level component overview for containers deployed in a Kubernetes cluster")

In {{site.data.keyword.Bluemix_notm}}, when you deploy applications in a Kubernetes cluster, consider the following information:

* In a {{site.data.keyword.Bluemix_notm}} account, you can have 1 or more organizations.
* Each organization can have 1 or more {{site.data.keyword.Bluemix_notm}} spaces.
* You can have 1 or more Kubernetes clusters in an organization.
* Collection of logs is enabled automatically when you create a Kubernetes cluster.
* A Kubernetes cluster is agnostic of {{site.data.keyword.Bluemix_notm}} spaces. However, the log data of a cluster and its resources is associated with a {{site.data.keyword.Bluemix_notm}} space.
* Log data is collected for an application as soon as the pod is deployed.
* To analyze log data for a cluster, you must access the Kibana dashboards for the Cloud Public region where the cluster is created.

Before you create a cluster, either through the [{{site.data.keyword.Bluemix_notm}} UI](../../../containers/cs_cluster.html#cs_cluster_ui) or through the [command line](../../../containers/cs_cluster.html#cs_cluster_cli), you must log into a specific {{site.data.keyword.Bluemix_notm}} region, account, organization, and space. The space where you are logged in is the space where logging data for the cluster and its resources is collected.

By default, information that any container process prints to stdout (standard output) and stderr (standard error) is collected. Sending information to stdout and stderr is the standard Docker convention for exposing the information of a container.

If you forward the log data of an app that runs in a container to the Docker log collector in JSON format, you can search and analyze log data in Kibana by using JSON fields. For more information, see [Configuring custom fields as Kibana search fields](logging_containers_ov.html#send_data_in_json).

**Note:** When you work with a Kubernetes cluster, the namespaces *ibm-system* and *kube-system* are reserved. Do not create, delete, modify, or change permissions of resources that are available in these namespaces. Logs for these namespaces are for {{site.data.keyword.IBM_notm}} use.

## Collecting logs for a container managed by Bluemix
{: #logging_containers_ov_logs_collected}

The following figure shows a high level view of logging for the {{site.data.keyword.containershort}}:

![High level component overview for containers deployed in a {{site.data.keyword.Bluemix_notm}}-managed cloud infrastructure](images/container_bmx.gif "High level component overview for containers deployed in a {{site.data.keyword.Bluemix_notm}}-managed cloud infrastructure")

By default, the following logs are collected for a container that is deployed in the {{site.data.keyword.Bluemix_notm}}-managed cloud infrastructure:

Table 2. Logs collected for containers deployed in the Bluemix-managed cloud infrastructure

      Log
      Description

      /var/log/messages
       By default, Docker messages are stored in the /var/log/messages folder of the container. This log includes system messages.

      ./docker.log
      This log is the Docker log.
 The Docker log file is not stored as a file inside of the container, but it is collected anyway. This log file is collected by default as it is the standard Docker convention for exposing the stdout (standard output) and stderr (standard error) information for the container. Information that any container process prints to stdout or stderr is collected.

To collect additional logs, add the **LOG_LOCATIONS** environment variable with a path to the log file when you create the container. You can add multiple log files by separating them with commas. For more information, see [Collecting non-default log data from a container](logging_containers_other_logs.html#logging_containers_collect_data).

##  Configuring custom fields as Kibana search fields
{: #send_data_in_json}

By default, logging is automatically enabled for containers. Every entry in the Docker log file is displayed in Kibana in the field `message`. If you need to filter and analyze your data in Kibana by using a specific field that is part of the container log entry, configure your application to send valid JSON formatted output.

Consider the following information:

* For containers that are deployed in a Kubernetes cluster, log the message in JSON format to stdout (standard output) and stderr (standard error).

    Each field that is available in the message is parsed to the type of field that matches is value. For example, each field in the following JSON message:

    ```
    {"field1":"string type",
        "field2":123,
        "field3":false,
        "field4":"4567"
    }
    ```

    is available as a field that you can use for filtering and searches:

    * `field1` is parsed as `field1_str` of type string.
    * `field2` is parsed as `field1_int` of type integer.
    * `field3` is parsed as `field3_bool` of type boolean.
    * `field4` is parsed as `field4_str` of type string.

* For containers that are deployed in the {{site.data.keyword.Bluemix_notm}}-managed cloud infrastructure, complete the following steps to parse container log entries into individual fields:

    1. Log the message to a file.
    2. Add the log file to the list of non-default logs that are available for analysis from a container. For more information, see [Collecting non-default log data from a container](logging_containers_other_logs.html#logging_containers_collect_data).

   If you log the message to a file, and a message is determined to be valid JSON, the fields are parsed and new fields are created for each field in the message. Only string-type field values are available for filtering and sorting in Kibana

## Viewing container logs for a container that runs in a Kubernetes cluster
{: #logging_containers_ov_methods_view_kube}

You can view the latest logs for a container in a Kubernetes pod by using any of the following methods:

* View logs through the Kubernetes UI. For each pod, you can select it and access its logs. For more information, see [Web UI Dashboard ![(External link icon)](../../../icons/launch-glyph.svg "External link icon")](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/){: new_window}.

* View logs by using the Kubernetes CLI command [kubectl logs ![(External link icon)](../../../icons/launch-glyph.svg "External link icon")](http://vishh.github.io/docs/user-guide/kubectl/kubectl_logs/){: new_window}.

To view long-term logs, you can use Kibana. Check the [Log retention](logging_containers_ov.html#logging_containers_ov_log_retention) information to learn about the data retention period policies.

## Viewing container logs for a container managed by Bluemix
{: #logging_containers_ov_methods_view_bmx}

You can view the latest logs for a container that is deployed in the {{site.data.keyword.Bluemix_notm}}-managed cloud infrastructure by using any of the following methods:

* View logs through the {{site.data.keyword.Bluemix_notm}} UI to monitor the latest activity of the container.

    You can view, filter, and analyze logs through the **Monitoring and Logs** tab that is available for each container. For more information, see [Analyzing logs from the Bluemix dashboard](../logging_view_dashboard.html#analyzing_logs_bmx_ui).

* View logs by using the {site.data.keyword.containershort}} CLI. Use commands to manage logs programmatically.

    You can view, filter, and analyze logs through the command line interface by using the **cf ic logs** command. For more information, see [Analyzing logs from the command line interface](../logging_view_cli.html#analyzing_logs_cli).

## Analyzing container logs
{: #logging_containers_ov_methods}

To analyze container log data, use Kibana to perform advanced analytical tasks. You can use Kibana, an open source analytics and visualization platform, to monitor, search, analyze, and visualize your data in a variety of graphs, for example charts and tables. For more information, see [Analyzing logs in Kibana](../kibana4/analyzing_logs_Kibana.html#analyzing_logs_Kibana).

## Log retention
{: #logging_containers_ov_log_retention}

Consider the following information about log retention:

* A maximum of 1 GB per space of data is stored per day. Any logs beyond that 1 GB cap are discarded. Cap allotments reset each
day at 12:30 AM UTC.

    You can increase your cap by contacting support. In the support ticket, include your space ID for the cap increase request, the new cap size, and the reason for the request.

* Up to 7 GB of data is searchable for a maximum of 7 days. Log data rolls over (First In, First Out) after either 7 GB of data is reached or 7 days.

## Tutorial: Analyze logs in Kibana for an app that is deployed in a Kubernetes cluster
{: #tutorial1}

To learn how to use Kibana to analyze the logs of a container that is deployed in a Kubernetes cluster, see [Tutorial: Analyze logs in Kibana for an app that is deployed in a Kubernetes cluster](tutorials/kibana_tutorial_1.html#kibana_tutorial_1).

repository: csantanapr/docs

repo_name: csantanapr/docs


text:
# Dapr Proof of Concept scenario

This document describes the scenario that will be developed to prove that [Dapr](https://dapr.io/) sidecars can be configured from a central place.

## Reasons

Kyma Runtimes must consume fewer resources. It can be achieved by delegating some of Kyma's responsibilities
to Dapr. Dapr injects sidecars into selected Pods where they fulfill their given tasks, such as state management or pub-sub.
Configuration for these sidecars can be held in one central place, from where Kyma Runtimes could fetch them and configure the
sidecars accordingly.

## Known issues

Dapr sidecars need a restart to apply changes in configuration. We are still waiting for the decision on the Dapr side if
they will stay with that approach.

## PoC scenario

The scenario will present two Kyma Runtimes using [Dapr HTTP Bindings](https://github.com/dapr/docs/blob/master/reference/specs/bindings/http.md) to reach two different Redis instances.

1. Kyma Control Plane admin sets the configuration for two Kyma Runtimes.
2. Runtime Agents from both Runtimes fetch the configuration and create the Dapr Bindings.
3. Kyma Control Plane admin changes the configuration.
4. Agents fetch the new configuration, apply it, and restart the Pods.
5. Both Runtimes use the new configuration.

As it's the Proof of Concept stage, we can use some temporary solutions, such as:
- Configuration held in memory instead of a database
- Pods being restarted by the Agent instead of a more complex solution

repository: colunira/control-plane

repo_name: colunira/control-plane


text:
Javascript allows you to extend native objects, which may sound appealing because your code will look better and simpler.
However, it can cause several problems, namely:

* In future versions, your method can accidentally override newly added built-ins
* If you are developing a library, your users may experience unexpected behaviour because your code is not contained
* It can lead to collisions and namespace conflicts.

repository: kylekellogg/codacy-jshint

repo_name: kylekellogg/codacy-jshint


text:
## Transition

The simplest animation is to use transition:

```js
// 让精灵在1s内向右移动50个像素
sprite.transition(1.0).attr({
  x: x => x + 50
})
```

`sprite.transition(sec).attr(...)` always returns a promise, so we can easily do continuous animations:

```js
const {Scene, Sprite} = spritejs;
const container = document.getElementById('adaptive');
const scene = new Scene({
  container,
  width: 1200,
  height: 600,
  // contextType: '2d',
});
const layer = scene.layer();

(async function () {
  const sprite = new Sprite({
    anchor: 0.5,
    bgcolor: 'red',
    pos: [500, 300],
    size: [200, 200],
    borderRadius: 50,
  });

  layer.append(sprite);

  await sprite.transition(2.0)
    .attr({
      bgcolor: 'green',
      width: width => width + 100,
    });

  await sprite.transition(1.0)
    .attr({
      bgcolor: 'orange',
      height: height => height + 100,
    });
}());
```

`sprite.transition(sec)` returns a transition object. It can call `attr()` multiple times. Each time it is called, it will automatically end the previous transition.

```js
const {Scene, Sprite, Path, Group} = spritejs;
const container = document.getElementById('adaptive');
const scene = new Scene({
  container,
  width: 1200,
  height: 600,
  // contextType: '2d',
});
const layer = scene.layer();

(async function () {
  await scene.preload([
    'https://p5.ssl.qhimg.com/t01f47a319aebf27174.png',
    'https://s3.ssl.qhres2.com/static/a6a7509c33a290a6.json',
  ]);

  const robot = new Sprite('guanguan1.png');
  robot.attr({
    anchor: 0.5,
    pos: [710, 210],
    scale: 0.4,
    rotate: 45,
    // zIndex: 2000,
  });
  layer.append(robot);

  const d = 'M235.946483,75.0041277 C229.109329,53.4046689 214.063766,34.845093 195.469876,22.3846101 C175.428247,8.9577702 151.414895,2 127.314132,2 C75.430432,2 31.6212932,32.8626807 18.323944,74.9130141 C8.97646468,77.1439182 2,85.5871171 2,95.7172992 C2,104.709941 7.49867791,112.371771 15.2700334,115.546944 C15.8218133,115.773348 16.6030463,122.336292 16.8270361,123.236385 C22.1235768,144.534892 35.4236577,163.530709 52.5998558,176.952027 C52.6299032,176.976876 52.6626822,177.001726 52.6954612,177.026575 C72.5513428,192.535224 98.5478246,202 127.043705,202 C152.034964,202 176.867791,194.597706 197.428422,180.146527 C215.659011,167.335395 230.201962,148.621202 236.52831,126.969284 C237.566312,123.421373 238.549682,119.685713 239.038636,116.019079 C239.044099,115.983185 239.074146,115.444787 239.082341,115.442025 C246.673412,112.184022 252,104.580173 252,95.7172992 C252,85.6892748 245.15192,77.3371896 235.946483,75.0041277';
  const shadowD = 'M82.1534529,43 C127.525552,43 164.306906,33.6283134 164.306906,21.663753 C164.306906,9.6991926 127.525552,0 82.1534529,0 C36.7813537,0 0,9.6991926 0,21.663753 C0,33.6283134 36.7813537,43 82.1534529,43 Z';
  const shadow = new Path();
  shadow.attr({
    d: shadowD,
    normalize: true,
    fillColor: '#000000',
    opacity: 0.05,
    pos: [500, 434],
    scale: [1.3, 1.2],
  });
  layer.append(shadow);

  const lemon = new Path();
  lemon.attr({
    d,
    normalize: true,
    pos: [500, 300],
    fillColor: '#fed330',
    scale: 1.4,
  });
  layer.append(lemon);

  const lemonGroup = new Group();
  lemonGroup.attr({
    anchor: 0.5,
    pos: [610, 300],
    size: [180, 180],
    bgcolor: '#faee35',
    border: [6, '#fdbd2c'],
    borderRadius: 90,
    scale: 1.5,
  });
  layer.append(lemonGroup);

  const d2 = 'M0,0L0,100A15,15,0,0,0,50,86.6z';
  for(let i = 0; i < 12; i++) {
    const t = new Path();
    t.attr({
      d: d2,
      scale: 0.65,
      lineWidth: 2,
      strokeColor: '#fff',
      fillColor: '#f8c32d',
      rotate: 30 * i,
    });
    lemonGroup.append(t);
  }

  lemonGroup.animate([
    {rotate: 360},
  ], {
    duration: 10000,
    iterations: Infinity,
  });

  const transition = robot.transition(0.3);

  lemonGroup.addEventListener('mouseenter', (evt) => {
    layer.timeline.playbackRate = 3.0;
    transition.attr({
      pos: [730, 190],
    });
  });
  lemonGroup.addEventListener('mouseleave', (evt) => {
    layer.timeline.playbackRate = 1.0;
    transition.attr({
      pos: [710, 210],
    });
  });
}());
```

## Web Animations API

Spritejs animation supports almost standard [web animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API)。

Each sprite has an `.animate` method, which is used to define and run animation. It returns an animation object, which has several different states, as follows:

| State | Description |
| --- | --- |
| idle | Before animation start. |
| pending | The subject is before moving or after moving but not finished yet. |
| running | The subject is moving. |
| paused | The animation is paused. |
| finished | The animation is finished |

According to the web animations API, the animate method has two parameters: a key frame sequence and a timing object.

Timeing object's properties as follows:

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| delay | Number | 0 | Delay time before subject moving. |
| endDelay | Number | 0 | Delay time after subject moving. |
| fill | Enum: 'none', 'forwards', 'backwards', 'both' | 'none' | If the attribute is `none`, the animation effect of the element is only valid in the `running` and `paused` states, and the element returns to the pre animation state in other states. If this attribute is `forwards`, then after the animation ends, the element remains at the end of the animation. If this attribute is `backwards`, then when the animation is in the pending state before the start, the element remains in the first frame of the animation. If this attribute is `both`, then the element maintains the first frame state before the animation starts and the last frame state after the animation ends. |
| iterations | Number | 1 | The number of times the animation is played, which can be an integer or a decimal. |
| direction | Enum: 'default', 'reverse', 'alternate', 'alternate-reverse' | 'default' | The direction of animation playback. If the attribute is set to 'reverse', the animation will play backward. If it is set to alternate, the animation will play forward and backward alternately when iterations > 1 |
| duration | Number | 0 | How long the animation playing once. |
| easing | String | 'linear' | The animation easing function, it can be`linear, ease, ease-in, ease-out, ease-in-out, step-start, step-end` or a cubic-bezier function such as `cubic-bezier(0.42, 0, 0.58, 1)` |

```js
const {Scene, Sprite} = spritejs;
const container = document.getElementById('adaptive');
const scene = new Scene({
  container,
  width: 1200,
  height: 600,
  // contextType: '2d',
});
const layer = scene.layer();

const sprite = new Sprite();
sprite.attr({
  anchor: [0.5, 0.5],
  pos: [600, 300],
  bgcolor: 'red',
  size: [50, 50],
  borderRadius: 25,
  translate: [0, -200],
  transformOrigin: [0, 200],
});

sprite.animate([
  {rotate: 0},
  {rotate: 360},
], {
  duration: 3000,
  iterations: Infinity,
});

layer.append(sprite);
```

## Timelined Animation

Sprite has a timeline attribute on its layer, which is a [Timeline](https://github.com/spritejs/sprite-timeline) object.All animations running on the layer use this timeline object to get the timeline, so when we change the timeline of the layer, we can affect the animation time of all elements.

```js
const {Scene, Sprite} = spritejs;
const container = document.getElementById('adaptive');
const scene = new Scene({
  container,
  width: 1200,
  height: 600,
  // contextType: '2d',
});
const layer = scene.layer();

(async function () {
  await scene.preload({id: 'snow', src: 'https://p5.ssl.qhimg.com/t01bfde08606e87f1fe.png'});

  const [speed1, speed2, speed4, halfSpeed, pause, reversePlay]
    = document.querySelectorAll('#speed1, #speed2, #speed4, #halfSpeed, #pause, #reversePlay');

  const timeline = layer.timeline;

  speed1.addEventListener('click', (evt) => {
    timeline.playbackRate = 1.0;
  });

  speed2.addEventListener('click', (evt) => {
    timeline.playbackRate = 2.0;
  });

  speed4.addEventListener('click', (evt) => {
    timeline.playbackRate = 4.0;
  });

  halfSpeed.addEventListener('click', (evt) => {
    timeline.playbackRate = 0.5;
  });

  pause.addEventListener('click', (evt) => {
    timeline.playbackRate = 0;
  });

  reversePlay.addEventListener('click', (evt) => {
    timeline.playbackRate = -1.0;
  });

  function addRandomSnow() {
    const snow = new Sprite('snow');
    const x0 = 20 + Math.random() * 1100,
      y0 = -100;

    snow.attr({
      anchor: [0.5, 0.5],
      pos: [x0, y0],
      size: [50, 50],
    });

    snow.animate([
      {x: x0 - 10},
      {x: x0 + 10},
    ], {
      duration: 1000,
      fill: 'forwards',
      direction: 'alternate',
      iterations: Infinity,
      easing: 'ease-in-out',
    });

    const dropAnim = snow.animate([
      {y: -100, rotate: 0},
      {y: 700, rotate: 360},
    ], {
      duration: 10000,
      fill: 'forwards',
    });

    dropAnim.finished.then(() => {
      snow.remove();
    });

    layer.append(snow);
  }

  setInterval(addRandomSnow, 200);
}());
```

When playback playbackrate < 0, the animation returns to the initial state and then animation ends, so the old snowflake floats upward, while the new snowflake animation ends at the beginning, so no new snowflake can be seen falling from above.

## Use third-party animation library

If you don't like the web animation API, you can also use third-party library, such as [TweenJS](https://github.com/tweenjs/tween.js).

```js
const {Scene, Sprite} = spritejs;
const container = document.getElementById('adaptive');
const scene = new Scene({
  container,
  width: 1200,
  height: 600,
  // contextType: '2d',
});
const layer = scene.layer();

const sprite = new Sprite();
sprite.attr({
  anchor: [0.5, 0.5],
  pos: [600, 300],
  bgcolor: 'rgb(128, 0, 255)',
  size: [100, 100],
});
layer.append(sprite);

const coords = {rotate: 0};

/* globals TWEEN */
new TWEEN.Tween(coords)
  .to({rotate: 360}, 5000)
  .easing(TWEEN.Easing.Quadratic.Out)
  .onUpdate(() => {
    const rotate = coords.rotate,
      radian = Math.PI * rotate / 180,
      red = 128 + Math.round(127 * Math.sin(radian)),
      green = Math.round(rotate) % 128,
      blue = 128 + Math.round(127 * Math.cos(radian));

    const bgcolor = `rgb(${red}, ${green}, ${blue})`;
    sprite.attr({rotate, bgcolor});
  })
  .repeat(Infinity)
  .start();

function animate() {
  requestAnimationFrame(animate);
  TWEEN.update(layer.timeline.currentTime);
}
requestAnimationFrame(animate);

const [speed1, speed2, speed4, halfSpeed, pause, reversePlay]
  = document.querySelectorAll('#tweenjs-speed1, #tweenjs-speed2, #tweenjs-speed4, #tweenjs-halfSpeed, #tweenjs-pause, #tweenjs-reversePlay');

const timeline = layer.timeline;

speed1.addEventListener('click', (evt) => {
  timeline.playbackRate = 1.0;
});

speed2.addEventListener('click', (evt) => {
  timeline.playbackRate = 2.0;
});

speed4.addEventListener('click', (evt) => {
  timeline.playbackRate = 4.0;
});

halfSpeed.addEventListener('click', (evt) => {
  timeline.playbackRate = 0.5;
});

pause.addEventListener('click', (evt) => {
  timeline.playbackRate = 0;
});

reversePlay.addEventListener('click', (evt) => {
  timeline.playbackRate = -1;
});
```

repository: spritejs/spritejs

gha_language: JavaScript

gha_license_id: MIT

repo_name: spritejs/spritejs


text:
---
hrs_structure:
  division: '3'
  volume: '12'
  title: '28'
  chapter: '505'
  section: ''
type: chapter
tags:
  - Property
  - Family
menu:
  hrs:
    identifier: HRS0505
    parent: title28
    name: Chapter 505 Uniform Federal Tax Lien Registration Act (Modified)
weight: 19000
title: Uniform Federal Tax Lien Registration Act (Modified)
full_title: Chapter 505 Uniform Federal Tax Lien Registration Act (Modified)
---
**CHAPTER 505**

**UNIFORM FEDERAL TAX LIEN REGISTRATION ACT**

**(MODIFIED)**

Section

505-1 Notices filed where

505-2 Index of liens

505-3 Certificates of release

505-4 Fees

Cross References

References to book and page, see §502-31.5.

repository: SamMade/BlueAndKrabby

repo_name: SamMade/BlueAndKrabby


text:
# Container Patterns

Building applications with container technologies like [Docker](https://www.docker.com/) or [rkt](https://github.com/coreos/rkt) allows for new architectures and decisions in application design. In this document we would like to start a discussion around this and share experiences.

### Context

We are mainly talking about **cloud aware applications**. Or as the [cloud native foundations](https://cncf.io/about/our-mission) summarizes: modern distributed systems which will be  _container packed_, _dynamically managed_ and _micro-services oriented_.

The goal is to find and describe general applicable patterns for building applications of this type. The patterns should be container runtime agnostic but with concrete examples.

We currently assume that using several containers is a good idea. The reasons for this are manifold and won't be discussed in detail here. But just a couple of reasons by way of example: “Independent releasable”, "Reuse of domain / infrastructure components”, “Separate processing types”, “Different loads”, “POCs isolation”, "Composability", “Use different languages / versions / libraries”, “Enforce architectural boundaries”, “Robustness / crash isolation” or simply “Different teams”.

### State

The current version is a discussion starter mainly taken from various feedback from the talks [@luebken](https://github.com/luebken) and [@denderello](https://github.com/denderello) have given. The latest version of the talk is from [QCon London (March 2016)](https://www.infoq.com/presentations/patterns-applications-containers). For the current state of this document please refer it's Github [issues](https://github.com/luebken/container-patterns/issues).

### Feedback

Currently most of the discussions are done in other channels. The first idea to consolidate this with [issues](https://github.com/luebken/container-patterns/issues). But feel free to suggest other channels.

## Overview Content
!INCLUDE "SUMMARY.md"

repository: bassista/container-patterns

repo_name: bassista/container-patterns


text:
[kakao](../../index.md) / [com.agoda.kakao](../index.md) / [KView](.)

# KView

`class KView : `[`KBaseView`](../-k-base-view/index.md)`<KView>`

Simple view with BaseActions and BaseAssertions

**See Also**

[BaseActions](../-base-actions/index.md)

[BaseAssertions](../-base-assertions/index.md)

### Constructors

| Name | Summary |
|---|---|
| [<init>](-init-.md) | `KView(function: `[`ViewBuilder`](../-view-builder/index.md)`.() -> `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)`)`
`KView(parent: Matcher<`[`View`](https://developer.android.com/reference/android/view/View.html)`>, function: `[`ViewBuilder`](../-view-builder/index.md)`.() -> `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)`)`
`KView(parent: DataInteraction, function: `[`ViewBuilder`](../-view-builder/index.md)`.() -> `[`Unit`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html)`)` |

### Inherited Properties

| Name | Summary |
|---|---|
| [view](../-k-base-view/view.md) | `open val view: ViewInteraction` |


text:
# nAnicitus

nAnicitus is a windows service that acts as a gatekeeper for the [SymStore][symstore_msdn] application. SymStore provides a relatively simple way to create a local / private symbol server with the disadvantage that it should really only be called by one user at the time because it doesn't support [multiple transactions at the same time][symstore_msdn_singletransaction]. The nAnicitus windows service 'serializes' access to SymStore to allow multiple users to add symbols to the symbol server.

Besides handling the the access to the symbol server nAnicitus also copies the sources into a UNC source location for access by a source server and performs [source indexing][sourceindexing_msdn] on the PDB files before they are stored in the symbol UNC path.

# Installation instructions
* Install [debugging tools for windows](http://msdn.microsoft.com/en-us/library/windows/hardware/gg463009.aspx). Make sure to install the complete set so that you get the symbol server tools.
* Install nAnicitus from the zip archive, which is available from the [releases page](https://github.com/pvandervelde/nAnicitus/releases).
* Update the configuration file with the paths to:
 * **DebuggingToolsDirectory:** The debugging tools directory (e.g. `c:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64`). This path may be left out if it is in the default location (as given here).
 * **SourceIndexUncPath:** The UNC path to the directory where the indexed sources will be placed (e.g. `\\MyServer\sources`).
 * **SymbolsIndexUncPath:** The UNC path to the directory where the indexed symbols (i.e. PDBs) will be placed (e.g. `\\MyServer\symbols`).
 * **ProcessedPackagesPath:** The directory where the NuGet symbol packages will be dropped after they are processed. The NuGet symbol packages are saved in this directory so that it is possible to reprocess the packages if desired at a later stage.
 * **UploadPath:** The directory where the NuGet symbol packages are placed for nAnicitus to process.
* To install the application as a windows service, open a command line window with administrative permissions, navigate to the nAnicitus install directory and execute the following command:

        Nanicitus.Service install

* Once the service is installed use the normal windows services control to change the properties of the service.

# Operation
Once the service is installed and started all you need to do to add symbols to the symbol server is to copy a NuGet symbol package to the upload directory. Note that the symbol package must have both PDB files __and__ [source files](http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-symbol-package).

While running nAnicitus will continuously monitor the upload directory for new files. Once a new file is detected the following steps will be taken.

1. nAnicitus unpacks the symbol package to a temporary location (`c:\users\<USER_NAME>\AppData\Local\Temp\Nanicitus.Core`).
* The PDB files are updated with a [srcsrv stream][srcsrv_stream].
* The source files are copied to the source UNC path.
* The PDB files are pushed to the symbol UNC path via SymStore.
* The symbol package is moved to the processed package directory.

During operation nAnicitus writes entries to a log file which contains information about all the steps taken to process a NuGet symbol package. The configuration file contains two settings that control the amount of information written to the log file and the location of the log file. These two settings are:

* **DefaultLogLevel:** The level of the log entries that should be written to the log file. Allowable log levels are:
 * Trace
 * Debug
 * Info
 * Warn
 * Error
 * Fatal
* **LogPath:** Defines the location of the log file. If no value is provided for this setting then the location will be: `C:\ProgramData\nAnicitus\Nanicitus.Service\0.1\logs`.

# How to build
The solution files are created in Visual Studio 2012 (using .NET 4.5) and the entire project can be build by invoking MsBuild on the nanicitus.integration.msbuild script. This will build the binaries and the ZIP archive. The binaries will be placed in the `build\bin\AnyCpu\Release` directory and the ZIP archive will be placed in the `build\deploy` directory.

Note that the build scripts assume that:

* The binaries should be signed, however the SNK key file is not included in the repository so a new key file has to be [created][snkfile_msdn]. The key file is referenced through an environment variable called `SOFTWARE_SIGNING_KEY_PATH` that has as value the full path of the key file.
* GIT can be found on the PATH somewhere so that it can be called to get the hash of the last commit in the current repository. This hash is embedded in the nAnicitus executable together with information about the build configuration and build time and date.

[symstore_msdn]: http://msdn.microsoft.com/en-us/library/windows/hardware/ff558848(v=vs.85).aspx
[symstore_msdn_singletransaction]: http://msdn.microsoft.com/en-us/library/windows/hardware/ff558851(v=vs.85).aspx
[sourceindexing_msdn]: http://msdn.microsoft.com/en-us/library/windows/hardware/ff556898(v=vs.85).aspx
[srcsrv_stream]: http://msdn.microsoft.com/en-us/library/windows/hardware/ff552219(v=vs.85).aspx
[snkfile_msdn]: http://msdn.microsoft.com/en-us/library/6f05ezxy(v=vs.110).aspx

repository: pvandervelde/nAnicitus

gha_language: C#

repo_name: pvandervelde/nAnicitus


text:
---
breadcrumbs:
- - /developers
  - For Developers
- - /developers/design-documents
  - Design Documents
page_name: conventions-and-patterns-for-multi-platform-development
title: Conventions and patterns for multi-platform development
---

Chromium is a large and complex cross-platform product. We try to share as much
code as possible between platforms, while implementing the UI and OS integration
in the most appropriate way for each. While this gives a better user experience,
it adds extra complexity to the code. This document describes the recommended
practices for keeping such cross-platform code clean.

We use a variety of different file naming suffixes to indicate when a file
should be used:

*   Mac files use the _mac suffix for lower-level files and Cocoa (Mac
            UI) files use the _cocoa suffix.
*   iOS files use the _ios suffix (although iOS also uses some specific
            _mac files).
*   Linux files use _linux suffix for lower-level files, _gtk for
            GTK-specific files, and _x for X Windows (with no GTK) specific
            files.
*   Chrome OS files use _chromeos suffix when needed but otherwise also
            include _linux files by default.
*   Windows files use the _win suffix.
*   Posix files shared between Mac, iOS, and Linux use the _posix
            suffix.
*   Files for Chrome's "Views" UI (on Windows and experimental GTK)
            layout system use the _views suffix.

Platform-specific suffixes are encoded using conditionals in their respective
BUILD.gn files:
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/no_sources_assignment_filter.md

The separate front-ends of the browser are contained in their own directories:

*   Mac Cocoa: chrome/browser/ui/cocoa
*   Linux GTK: chrome/browser/ui/gtk
*   Windows Views (and the experimental GTK-views):
            chrome/browser/ui/views

The [Coding Style](/developers/coding-style) page lists some stylistic rules
affecting platform-specific defines.

### How to separate platform-specific code

#### Small platform differences: #ifdefs

When you have a class with many shared functions or data members, but a few
differences, use #ifdefs around the platform-specific parts. If there are no
significant differences, it's easier for everybody to keep everything in one
place.

#### Small platform differences in the header, larger ones in the implementation: split the implementation

There may be cases where there are few header file differences, but significant
implementation differences for parts of the implementation. For example,
base/waitable_event.h defines a common API with a couple of platform
differences.

With significant implementation differences, the implementation files can be
split. The prevents you from having to do a lot of #ifdefs for the includes
necessary for each platform and also makes it easier to follow (three versions
each of a set of functions in a file can get confusing). There can be different
.cc files for each platform, as in base/waitable_event_posix.cc that implements
posix-specific functions. If there were cross-platform functions in this class,
they would be put in a file called base/waitable_event.cc.

#### Complete platform implementations and callers: separate implementations

When virtually none of the implementation is shared, implement the class
separately for each platform in separate files.

If all implementations are in a cross-platform directory such as base, they
should be named with the platform name, such as FooBarWin in base/foo_bar_win.h.
This case will generally be rare since files in these cross-platform files are
normally designed to be used by cross-platform code, and separate header files
makes this impossible. In some places we've defined a commonly named class in
different files, so PlatformDevice is defined in skia/ext/platform_device_win.h,
skia/ext/platform_device_linux.h, and skia/ext/platform_device_mac.h. This is OK
if you really need to refer to this class in cross-platform code. But generally,
cases like this will fall into the following rule.

If the implementations live in platform-specific directories such as
chrome/browser/ui/cocoa or chrome/browser/ui/views, there is no chance that the
class will be used by cross-platform code. In this case, the classes and
filenames should omit the platform name since it would be redundant. So you
would have FooBar implemented in chrome/browser/ui/cocoa/foo_bar.h.

Don't create different classes with different names for each platform and
typedef it to a shared name. We used to have this for PlatformCanvas, where it
was a typedef of PlatformCanvasMac, PlatformCanvasLinux, or PlatformCanvasWin
depending on the platform. This makes it impossible to forward-declare the
class, which is an important tool for reducing dependencies.

### When to use virtual interfaces

In general, virtual interfaces and factories should not be used for the sole
purpose of separating platform differences. Instead, it should be be used to
separate interfaces from implementations to make the code better designed. This
comes up mostly when implementing the view as separate from the model, as in
TabContentsView or RenderWidgetHostView. In these cases, it's desirable for the
model not to depend on implementation details of the view. In many cases, there
will only be one implementation of the view for each platform, but gives cleaner
separation and more flexibility in the future.

In some places like TabContentsView, the virtual interface has non-virtual
functions that do things shared between platforms. Avoid this. If the code is
always the same regardless of the view, it probably shouldn't be in the view in
the first place.

### Implementing platform-specific UI

In general, construct platform specific user interface elements from other
platform-specific user interface elements. For instance, the views-specific
class BrowserView is responsible for constructing many of the browser dialog
boxes. The alternative is to wrap the UI element in a platform-independent
interface and construct it from a model via a factory. This is significantly
less desirable as it confuses ownership: in most cases of construction by
factory, the UI element returned ends up being owned by the model that created
it. However in many cases the UI element is most easily managed by the UI
framework to which it belongs. For example, a views::View is owned by its view
hierarchy and is automatically deleted when its containing window is destroyed.
If you have a dialog box views::View that implements a platform independent
interface that is then owned by another object, the views::View instance now
needs to explicitly tell its view hierarchy not to try and manage its lifetime.

e.g. prefer this:

> // browser.cc:
> Browser::ExecuteCommand(..) {
> ...
> case IDC_COMMAND_EDIT_FOO:
> window()->ShowFooDialog();
> break;
> ...
> }
> // browser_window.h:
> class BrowserWindow {
> ...
> virtual void ShowFooDialog() = 0;
> ...
> };
> // browser_view.cc:
> BrowserView::ShowFooDialog() {
> views::Widget::CreateWindow(new FooDialogView)->Show();
> }
> // foo_dialog_view.cc:
> // FooDialogView and FooDialogController are automatically cleaned up when the
> window is closed.
> class FooDialogView : public views::View {
> ...
> private:
> scoped_ptr<FooDialogController> controller_; // Cross-platform
> state/control logic
> ...
> }

to this:

> // browser.cc:
> Browser::ExecuteCommand(..) {
> ...
> case IDC_COMMAND_EDIT_FOO: {
> FooDialogController::instance()->ShowUI();
> break;
> }
> ...
> }
> // foo_dialog_controller.h:
> class FooDialog {
> public:
> static FooDialog\* CreateFooDialog(FooDialogController\* controller);
> virtual void Show() = 0;
> virtual void Bar() = 0;
> };
> class FooDialogController {
> public:
> ...
> static FooDialogController\* instance() {
> static FooDialogController\* instance = NULL;
> if (!instance)
> instance = Singleton<FooDialogController>::get();
> return instance;
> }
> ...
> private:
> ...
> void ShowUI() {
> if (!dialog_.get())
> dialog_.reset(FooDialog::CreateFooDialog(this));
> dialog_->Show();
> }
> // Why bother keeping FooDialog or even FooDialogController around?
> // Most dialogs are very seldom used.
> scoped_ptr<FooDialog> dialog_;
> };
> // foo_dialog_win.cc:
> class FooDialogView : public views::View,
> public FooDialogController {
> public:
> ...
> explicit FooDialogView(FooDialogController\* controller) {
> set_parent_owned(false); // Now necessary due to scoped_ptr in
> FooDialogController.
> }
> ...
> };
> FooDialog\* FooDialog::CreateFooDialog(FooDialogController\* controller) {
> return new FooDialogView(controller);
> }

Sometimes this latter pattern is necessary, but these occasions are rare, and
very well understood by the frontend team. When porting, consider converting
cases of the latter model to the former model if the UI element is something
simple like a dialog box.

repository: iridium-browser/iridium-browser

repo_name: iridium-browser/iridium-browser


text:
# Opal Dark UI for Atom

> A dark, colorful, focus-oriented theme with opal-colored accents contrasted against an indigo-night editor background.

![Screenshot](sample_ui.png)

## Features

1. Active and hovered tabs are underlined thickly with cyan.
2. Larger-than normal pink circles form instead of the close icon on modified tabs, and the large cyan close icon flashes after saving the modified file.
3. Gray-violet base accent colors, used for the scrollbars, button border colors, and tooltips.
4. Scrollbars flash pink when selected and dragged, mimicking an opal's facets.
5. Indigo-themed autocomplete dropdown list with a lighter indigo accent for even selections.
6. Very dark indigo editor background matches the dropdown and doesn't clash with the dark-gray panel colors.
7. Buttons flash indigo upon mouse hover, matching the indigo-patterned autocomplete dropdown and editor background.
8. Modified files are colored pink in the tree-view, and successfully-created files are colored soft-cyan.

## Install

Install using Atom

1. Go to Atom -> Preferences...
2. Then select the Install tab
3. Click the Themes button to the right of the search box
4. Enter opal-dark-ui in the search box.

## Team

Modified and maintained by [statisticallyfit](https://github.com/statisticallyfit/opal-dark-ui/graphs/contributors).

This theme is based off the Dracula Theme. Work from original creators is visible [here](https://github.com/dracula/atom-ui/graphs/contributors).

## License

[MIT License](./LICENSE)

repository: statisticallyfit/opal-dark-ui

repo_name: statisticallyfit/opal-dark-ui


text:
# os-inventory: an OpenShift installation inventory generator

## DISCLAIMER: This project is deprecated.
Red Hat OpenShift 4 drop the Ansible deployment method and introduces a totally
new approach in the installation of OpenShift clusters based on Terraform and a
combination of tecniques which leverage on Red Hat CoreOS **Ignition Configs**
on boostrap nodes and control plane nodes.
Ansible roles are now only related to worker nodes based on RHEL.

## Why?

Red Hat OpenShift Container Platform is installed using Ansible. The community project
https://github.com/openshift/openshift-ansible provides related playbooks and roles.
To deploy OpenShift a lot of parameters can be set using inventory variables.
To speed up the deployment process, a tool that creates inventories from a template can
be really helpful. Written in Go, si single binary tool can generate custom inventories
starting from a simple yaml file with a minimun set of parameters.
Default values are provided to generate a basic inventory from scratch.
Despite being a very simple tool, os-inventory implements a full working CLI that can be
extended with new features in the future, yet maintaining the KISS phylosophy.

## Build and Install

To build and install os-inventory:

```
$ make
$ sudo make install
```

This will run tests and compile the binary install it under */usr/local/bin* on your system.

## Usage

To get the generic help and overall description simply run the command without arguments:

```
$ os-inventory
```

To print a specific command help:

```
$ os-inventory <COMMAND> --help
```

To dump all the default values in YAML format to stdout:

```
$ os-inventory defaults
```

To generate an inventory and dump it to stdout:

```
$ os-inventory generate
```

To generate the inventory loading configurations from a YAML file:

```
$ os-inventory generate -f examples/example.yml
```

The generated inventory is redirected by defayult to stdout. To create a custom file
use the options *-o* or *--output*:

```
$ os-inventory generate -f example.yml -o /tmp/myinventory
```

## The YAML config

The YAML configuration file can include a different number of parameters:

```
---
deployment_type: "enterprise"
multi_master: true
haproxy_enabled: true
masters_list:
    - m1.example.com
    - m2.example.com
    - m3.example.com
etcd_list:
    - m1.example.com
    - m2.example.com
    - m3.example.com
lb_list:
    - lb.example.com
nodes_map:
    m1.example.com: ""
    m2.example.com: ""
    m3.example.com: ""
    n1.example.com: "openshift_node_labes=\"{'region': 'infra'}\""
    n2.example.com: "openshift_node_labes=\"{'region': 'infra'}\""
    n3.example.com: "openshift_node_labes=\"{'region': 'infra'}\""
    n4.example.com: "openshift_node_labes=\"{'region': 'primary'}\""
    n5.example.com: "openshift_node_labes=\"{'region': 'primary'}\""
```

A more minimalistic config file can be produces, maybe with just one custom parameter:

```
---
deployment_type: "origin"
```

It is possible to generate CNS ready configurations. This is a small example of Registry
using Container Native Storage, a very common scenario:

```
---
deployment_type: "enterprise"
multi_master: true
haproxy_enabled: true
nfs_enabled: false
glusterfs_registry_enabled: true
registry_cns: true
registry_native_nfs: false
masters_list:
    - m1.example.com
    - m2.example.com
    - m3.example.com
etcd_list:
    - m1.example.com
    - m2.example.com
    - m3.example.com
lb_list:
    - lb.example.com
glusterfs_registry_map:
    n1.example.com: "glusterfs_devices='[ \"/dev/vdb\", \"/dev/vdc\", \"/dev/vdd\" ]'"
    n2.example.com: "glusterfs_devices='[ \"/dev/vdb\", \"/dev/vdc\", \"/dev/vdd\" ]'"
    n3.example.com: "glusterfs_devices='[ \"/dev/vdb\", \"/dev/vdc\", \"/dev/vdd\" ]'"
nodes_map:
    m1.example.com: ""
    m2.example.com: ""
    m3.example.com: ""
    n1.example.com: "openshift_node_labes=\"{'region': 'infra'}\""
    n2.example.com: "openshift_node_labes=\"{'region': 'infra'}\""
    n3.example.com: "openshift_node_labes=\"{'region': 'infra'}\""
    n4.example.com: "openshift_node_labes=\"{'region': 'primary'}\""
    n5.example.com: "openshift_node_labes=\"{'region': 'primary'}\""
```

## Defaults

Currently, the configured defaults are the following:

```
---
deployment_type: origin     # Choose between "origin" or "enterprise" deployment
ssh_user: root              # The user that connects to the nodes
nfs_enabled: true           # Use native NFS for storage
glusterfs_enabled: false    # Enable Glusterfs based CNS or CRS
glusterfs_registry_enabled: false   # Use Glusterfs for registry storage
registry_native_nfs: true   # Use NFS for registry storage
registry_cns: false         # Enable Container Native Storage for registy
haproxy_enabled: false      # Enable HAProxy loadbalancer in HA deployments
install_version: v3.9       # Installation version
skip_checks: false          # Skip storage, memory, package checks
multi_master: false         # Multi master HA deployment
cluster_method: native      # Cluster method
cluster_hostname: osapi.example.com # Private cluster endpoint name for API
cluster_public_hostname: osapi.example.com  # Public cluster endpoint name for API
containerized_deploy: false # Enable containerized deployment
containerized_ovs: false    # Containerize OpenvSwitch
containerized_node: false   # Containerize atomic-openshift-node service
containerized_master: false # Containerize master services (api and controllers)
containerized_etcd: false   # Containerize etcd service
system_images_registry: registry.access.redhat.com  # Default system images registry
openshift_use_crio: false   # Enable CRI-O deployment
openshift_crio_use_rpm: false   # Use rpm basesd installation for CRI-O
multi_infra: false          # Deploy multiple infra nodes
use_xip: false              # Use XIP for wildcard DNS (useful in training labs)
infra_ipv4: ""              # Infra IPv4 address for XIP based wildcard
ext_dns_wildcard: osapps.example.com    # Wildcard address (the common scenario)
sdn_plugin: ovs-multitenant # Cluster SDN plugin
disable_servicecatalog: true    # Disable the Service Catalog (from v3.7)
infra_replicas: 3           # Number of replicas for infra nodes
metrics_enabled: true       # Enable metrics subsystem
deploy_hosa: true           # Deploy Hawkular OpenShift Agent (HOSA)
metrics_native_nfs: true    # Use cluster NFS for metrics storage
prometheus_enabled: false   # Enable Prometheus deployment
prometheus_native_nfs: true # Use cluster NFS for Prometheus
logging_enabled: true       # Enable logging subsystem
logging_native_nfs: true    # Use Cluster NFS for logging
masters_list: []            # List of the master nodes
etcd_list: []               # List of the etcd nodes
lb_list: []                 # List of the load balancers
nfs_list: []                # List of cluster NFS nodes
glusterfs_map: {}           # Key/Value map of GlusterFS nodes
glusterfs_registry_map: {}  # Key/Value map of GlusterFS nodes for registry
nodes_map: {}               # Key/Value map of nodes.
```

## TODO

- Create new entries in the template for Container Native Storage (CNS)
- Create more sanity checks on string and int values
- Implement a good code testing

repository: giannisalinetti/os-inventory

repo_name: giannisalinetti/os-inventory


text:
# Lab 3. Using IBM Cloud Block Storage with Kubernetes

## Introduction

When looking at what kind of storage class you would like to use in Kubernetes, there are are a few choices such as file storage, block storage, object storage, etc. If your use case requires fast and reliable data access then consider block storage.

[Block storage](https://www.ibm.com/cloud/learn/block-storage) is a storage option that breaks data into "blocks" and stores those blocks across a Storage Area Network (SAN). These smaller blocks are faster to store and retrieve than large data objects. For this reason, block storage is primarily used as a backing storage for databases.

In this lab we will deploy a Mongo database on top of block storage on Kubernetes.

![archDiagram](./images/archDiagram.png)

The basic architecture is as follows

1. When we install MongoDB with the helm chart, a `Persistent Volume Claim` (PVC) is created on the cluster. This PVC is a request for storage to be used by the application.

2. In IBM Cloud, the request goes to the IBM Cloud storage provider which then provisions a physical storage device within IBM Cloud.

3. A `Persistent Volume` (PV) is then created which acts as a reference to the physical storage device created earlier. This PV is then mounted as a directory in a container's file system.

4. The guestbook application receives requests to store guestbook entries from the user which the guestbook pod then sends to the MongoDB pod to store.

5. The MongoDB pod receives the request to store information and persists the data to the mounted directory from the Persistent Volume.

## Using IBM Cloud Block Storage with Kubernetes

Log into the Kubernetes cluster and create a project where we want to deploy our application.

```bash
kubectl create namespace mongo
```

## Install Block Storage Plugin

By default IBM Kubernetes Service Clusters don't have the option to deploy block storage persistent volumes. However, there is an easy process to add the block storage `storageClass` to your cluster through the use of an automated helm chart install.

1. Follow the steps outlined [here](https://cloud.ibm.com/docs/containers?topic=containers-block_storage#install_block) to install the block storage `storageClass`.

1. First you need to add the `iks-charts` helm repo to your local helm repos. This will allow you to utilize a variety of charts to install software on the IBM Kubernetes Service.

    ```bash
    helm repo add iks-charts https://icr.io/helm/iks-charts
    ```

1. Then, we need to update the repo to ensure that we have the latest charts:

    ```bash
    helm repo update
    ```

1. Install the block storage plugin from the `iks-charts` repo:

    ```bash
    helm install block-storage-plugin iks-charts/ibmcloud-block-storage-plugin
    ```

1. Lastly, verify that the plugin installation was successful by retrieving the list of storage classes in the cluster:

    ```bash
    kubectl get storageclasses
    ```

    You should notice a few options that start with `ibmc-block` as seen below.

    ```bash
    NAME                       PROVISIONER         RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    ibmc-block-bronze          ibm.io/ibmc-block   Delete          Immediate           true                   62s
    ibmc-block-custom          ibm.io/ibmc-block   Delete          Immediate           true                   62s
    ibmc-block-gold            ibm.io/ibmc-block   Delete          Immediate           true                   62s
    ```

## Helm Repo setup

The lab uses Bitnami's Mongodb [Helm chart](https://github.com/bitnami/charts/tree/master/bitnami/mongodb) to show case the use of block storage. Set the Bitnami helm repo prior to installing mongodb.

```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
```

Expected output:

```bash
$ helm repo add bitnami https://charts.bitnami.com/bitnami

"bitnami" has been added to your repositories
```

Validate the repo is available in the list.

```bash
helm repo list
```

You should see a list of repos available to you as seen below:

```bash
NAME       URL
bitnami    https://charts.bitnami.com/bitnami
iks-charts https://icr.io/helm/iks-charts
```

## Mongodb with block storage

### Installation Dry Run

Before we install MongoDB, let's do a test of the installation to see what the chart will create. Since we are using Helm to install MongoDB, we can make use of the `--dry-run` flag in our `helm install` command to show us the manifest files that Helm will apply on the cluster.

Dryrun:

```bash
helm install mongo bitnami/mongodb --set global.storageClass=ibmc-block-gold,auth.password=testing,auth.username=guestbookAdmin,auth.database=guestbook -n mongo --dry-run > mongdb-install-dryrun.yaml
```

>There is a detailed breakdown of this command in the next section titled `Installing MongoDB` if you would like to understand what this helm command is doing.

This command will test out our helm install command and save the output manifests in a file called `mongodb-install-dryrun.yaml`. You can then examine this manifest file so that you know exactly what will be installed on your cluster.

Check out the file in your code editor and take a look at the `PersistentVolumeClaim` object. There should be a property named `storageClassName` in the spec and the value should be `ibmc-block-gold` to signify that we will be using block storage for our database.

Below is what that `PersistentVolumeClaim` object should look like.

```yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mongo-mongodb
  namespace: mongo
  labels:
    app.kubernetes.io/name: mongodb
    helm.sh/chart: mongodb-10.0.4
    app.kubernetes.io/instance: mongo
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: mongodb
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: "8Gi"
  storageClassName: ibmc-block-gold
```

### Install Mongodb

Before we install MongoDB we need to generate a password for our database credentials. These credentials will be used in the application to authenticate with the database.

For this lab, will be using the [openssl](https://www.openssl.org/) tool to generate the password as this is a common open source cryptographic library. The rest of the command will strip out any characters that could cause issues with the password.

```bash
USER_PASS=`openssl rand -base64 12 | tr -d "=+/"`
```

Now we can install MongoDB and supply the password that we just generated.

``` bash
helm install mongo bitnami/mongodb --set global.storageClass=ibmc-block-gold,auth.password=$USER_PASS,auth.username=guestbook-admin,auth.database=guestbook -n mongo
```

Here's an explanation of the above command:

- `helm install mongo bitnami/mongo`: Install the MongoDB bitnami helm chart and name the release "mongo".
- `--set global.storageClass=ibmc-block-gold`: Set the storage class to block storage rather than the default file storage.
- ...`auth.password=$USER_PASS`: Create a custom user with this password (Which we generated earlier).
- ...`auth.username=guestbook-admin`: Create a custom user with this username.
- ...`auth.database=guestbook`: Create a database named `guestbook` that the custom user can authenticate to.
- `-n mongo`: Install this release in the `mongo` namespace.

Expected output:

```bash
NAME: mongo
LAST DEPLOYED: Tue Nov 24 10:41:15 2020
NAMESPACE: mongo
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
...
```

View the objects being created by the helm chart.

```bash
kubectl get all -n mongo
```

```bash
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
service/mongo-mongodb   ClusterIP   <IP_ADDRESS>   <none>        27017/TCP   17s

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mongo-mongodb   0/1     0            0           17s

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/mongo-mongodb-6f8f7cd789   1         0         0       17s
```

View the list of persistence volume claims. Note that the `mongo-mongodb` is pending volume allocation.

```bash
kubectl get pvc -n mongo
```

```bash
NAME            STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS      AGE
mongo-mongodb   Pending                                      ibmc-block-gold   21s
```

After waiting for some time. The pod supporting Mongodb should have a  `Running` status.

```bash
$ kubectl get all -n mongo
NAME                                 READY   STATUS    RESTARTS   AGE
pod/mongo-mongodb-66d7bcd7cf-vqvbj   1/1     Running   0          8m37s

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
service/mongo-mongodb   ClusterIP   <IP_ADDRESS>   <none>        27017/TCP   12m

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mongo-mongodb   1/1     1            1           12m

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/mongo-mongodb-66d7bcd7cf   1         1         1       8m37s
replicaset.apps/mongo-mongodb-6f8f7cd789   0         0         0       12m
```

And the PVC `mongo-mongodb` is now bound to volume `pvc-2f423668-4f87-4ae4-8edf-8c892188b645`

```bash
$ kubectl get pvc -n mongo
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
mongo-mongodb   Bound    pvc-2f423668-4f87-4ae4-8edf-8c892188b645   20Gi       RWO            ibmc-block-gold   2m26s
```

With MongoDB deployed now we need to deploy an application that will utilize it as a datastore.

### Building Guestbook

For this lab we will be using the guestbook application which is a common sample kubernetes application. However, the version that we are using has been refactored as a loopback application.

1. The image for `Guestbook` to run with `MongoDB` was built with following configuration in the [datasources.json](https://github.com/IBM/guestbook-nodejs/blob/mongo-db/src/server/datasources.json) file.:

    ```json
    {
      "in-memory": {
        "name": "in-memory",
        "localStorage": "",
        "file": "",
        "connector": "memory"
      },
      "mongo": {
        "host": "${MONGO_HOST}",
        "port": "${MONGO_PORT}",
        "url": "",
        "database": "${MONGO_DB}",
        "password": "${MONGO_PASS}",
        "name": "mongo",
        "user": "${MONGO_USER}",
        "useNewUrlParser": true,
        "connector": "mongodb"
      }
    }
    ```

    This file will contain the connection information to our MongoDB instance. These variables will be passed into the environment from ConfigMaps and Secrets that we will create.

1. The `server/model-config.json` file, the `entry.datasource` value was set to `mongo` as seen below:

    ```json
    ...
    "entry": {
        "dataSource": "mongo",
        "public": true
      }
    }
    ```

    In this file we are telling the application which datasource we should use; in-memory or MongoDB. By default the application comes with an in-memory datastore for storing information but this data does not persist after the application crashes or if the pod goes down for any reason. We are changing `in-memory` to `mongo` so that the data will persist in our MongoDB instance external to the application so that the data will remain even after the application crashes.

### Deploying Guestbook

Now that we have built our application, let's check out the manifest files needed to deploy it to Kubernetes.

1. Review manifest files.

    This repo contains 3 manifests that we will be deploying to our cluster today:

    - A deployment manifest
    - A service manifest
    - A configMap manifest

    These manifests will create their respective kubernetes objects on our cluster.

    The `deployment` will deploy our application image that we built earlier while the `service` will expose that application to external traffic. The `configMap` will contain connection information for our database such as database hostname and port.

1. Open the `guestbook-deployment.yaml` and review the image name

    Line 25 in my `guestbook-deployment.yaml` file would look like this:

    ```yaml
    ...
      image: rojanjose/guestbook-nodejs:mongo
    ...
    ```

    As part of the deployment, kubernetes will copy the database connection information from the configMap into the environment of the application. You can see where this is specified in the `env` section of the deployment manifest as seen below:

    ```yaml
    ...
    env:
      - name: MONGO_HOST
        valueFrom:
          configMapKeyRef:
            name: mongo-config
            key: mongo_host
      - name: MONGO_PORT
        valueFrom:
          configMapKeyRef:
            name: mongo-config
            key: mongo_port
      - name: MONGO_USER
        valueFrom:
          secretKeyRef:
            name: mongodb
            key: username
      - name: MONGO_PASS
        valueFrom:
          secretKeyRef:
            name: mongodb
            key: password
      - name: MONGO_DB
        valueFrom:
          configMapKeyRef:
            name: mongo-config
            key: mongo_db_name
    ```

    You might also notice that we are getting our database username (`MONGO_USER`) and password (`MONGO_PASS`) from a kubernetes secret. We haven't defined that secret yet so let's do it now.

    ```bash
    kubectl create secret generic mongodb --from-literal=username=guestbook-admin --from-literal=password=$USER_PASS -n mongo
    ```

1. Now we are ready to deploy the application. Run the following commands:

    ```bash
    cd ../lab3
    kubectl apply -f . -n mongo
    ```

    Ensure that the application pod is running:

    ```bash
    kubectl get pods -n mongo
    ```

    You should see both the mongo pod and the guestbook pod running now:

    ```bash
    NAME                             READY   STATUS    RESTARTS   AGE
    guestbook-v1-9465dcbb4-zdhqv     1/1     Running   0          19s
    mongo-mongodb-757d9777d7-j4759   1/1     Running   0          27m
    ```

### Test out the application

Now that we have deployed the application, let's test it out.

1. Find the URL for the guestbook application by joining the worker node external IP and service node port. Run the following to get the IP and service node port of the application:

    ```bash
    HOSTNAME=`kubectl get nodes -ojsonpath='{.items[0].metadata.labels.ibm-cloud\.kubernetes\.io\/external-ip}'`
    SERVICEPORT=`kubectl get svc guestbook -n mongo -o=jsonpath='{.spec.ports[0].nodePort}'`
    echo "http://$HOSTNAME:$SERVICEPORT"
    ```

1. In your browser, open up the address that was output as part of the previous command.

1. Type in a few test entries in the text box and press enter to submit them.

    ![testEntries](./images/testEntries.png)

    These entries are now saved in the Mongo database. Let's take down the application and see if the data will truly persist.

1. Find the name of the pod that is running our application:

    ```bash
    kubectl get pods -n mongo
    ```

    Copy the name of the pod that starts with `guestbook`. For me, the pod is named `guestbook-v1-9465dcbb4-f6s9h`.

    ```bash
    NAME                             READY   STATUS    RESTARTS   AGE
    guestbook-v1-9465dcbb4-f6s9h     1/1     Running   0          4m7s
    mongo-mongodb-757d9777d7-q64lg   1/1     Running   0          5m47s
    ```

1. Then, run the following command, replacing `<pod name>` with pod name that you just copied.

    ```bash
    kubectl delete pod -n mongo <pod name>
    ```

    You should then see a message saying that your pod has been deleted.

    ```bash
    $ kubectl delete pod -n mongo guestbook-v1-9465dcbb4-f6s9h
    pod "guestbook-v1-9465dcbb4-f6s9h" deleted
    ```

1. Now, view your pods again:

    ```bash
    kubectl get pods -n mongo
    ```

    You should see the guestbook pod is back now with and the age has been reset. This means that it is a brand new pod that kubernetes has deployed automatically after our previous pod was deleted.

    ```bash
    NAME                             READY   STATUS    RESTARTS   AGE
    guestbook-v1-9465dcbb4-8z8bt     1/1     Running   0          87s
    mongo-mongodb-757d9777d7-q64lg   1/1     Running   0          9m13s
    ```

1. Refresh your browser tab that had the guestbook application and you will see that your data has indeed persisted after our pod went down.

    ![new pod](./images/newPod.png)

## Summary

In this lab we used block storage to run our own database on Kubernetes. Block storage allows for fast I/O operations making it ideal for our application database. We utilized configMaps and secrets to store the database configuration making it easy to use this application with different database configurations without making code changes.

## Cleanup (Optional)

This part of the lab desrcibes the steps to delete what was built in the lab.

repository: jrtorres/iks-cloudnativeworkshop

gha_language: Shell

repo_name: jrtorres/iks-cloudnativeworkshop