Creating and Editing Pages
This page shows how to create, test, and maintain Istio documentation topics.
Before you begin
Before you can work on Istio documentation, you first need to create a fork of the Istio documentation repository as described in Working with GitHub.
Choosing a page type
As you prepare to write a new topic, think about which of these page types is the best fit for your content:
Concept | A concept page explains some significant aspect of Istio. For example, a concept page might describe the Mixer's configuration model and explain some of its subtleties. Typically, concept pages don't include sequences of steps, but instead provide links to tasks that do. |
Reference | A reference page provides exhaustive lists of things like API parameters, command-line options, configuration settings, and procedures. |
Examples | An example page describes a fully working stand-alone example highlighting a particular set of features. Examples must have easy to follow setup and usage instructions so users can quickly run the sample themselves and experiment with changing the example to explore the system. Examples are tested and maintained for technical accuracy. |
Task | A task page shows how to do a single thing, typically by giving a short sequence of steps. Task pages have minimal explanation, but often provide links to conceptual topics that provide related background and knowledge. Tasks are tested and maintained for technical accuracy. |
Setup | A setup page is similar to a task page, except that it is focused on installation activities. Setup pages are tested and maintained for technical accuracy. |
Blog Post | A blog post is a timely article on Istio or products and technologies related to it. Typically, posts fall in one of the following four categories:
|
FAQ | A FAQ entry is for quick answers to common customer questions. An answer doesn't normally introduce any new concept and is instead narrowly focused on some practical bit of advice or insight, with links back into the main documentation for the user to learn more. |
Ops Guide | For practical solutions to address specific problems encountered while running Istio in a real-world setting. |
Naming a topic
Choose a title for your topic that has the keywords you want search engines to find. Create a filename for your topic that uses the words in your title, separated by hyphens, all in lower case.
Updating front matter
Every documentation file needs to start with front matter. The front matter is a block of YAML that is between triple-dashed lines at the top of each file. Here’s the chunk of front matter you should start with:
---
title: <title>
description: <description>
weight: <weight>
keywords: [keyword1,keyword2,...]
---
Copy the above at the start of your new markdown file and update the information fields. The available front matter fields are:
Field | Description |
---|---|
title | The short title of the page |
linktitle | An alternate, typically shorter, title for the page which is used in the side bar to reference the page |
subtitle | An optional subtitle which gets displayed below the main title |
description | A one-line description of what the page is about |
icon | An optional path to an image file which gets displayed next to the main title |
weight | An integer used to determine the sort order of this page relative to other pages in the same directory |
keywords | An array of keywords describing the page, used to create the web of See Also links |
draft | When true, prevents the page from showing up in any navigation area |
aliases | See Renaming, moving, or deleting pages below for details on this item |
skip_toc | Set this to true to prevent the page from having a table of contents generated for it |
skip_byline | Set this to true to prevent the page from having a byline under the main title |
skip_seealso | Set this to true to prevent the page from having a “See also” section generated for it |
force_inline_toc | Set this to true to force the generated table of contents to be inserted inline in the text instead of in a sidebar |
simple_list | Set this to true to force a generated section page to use a simple list layout rather that a gallery layout |
content_above | Set this to true to force the content portion of a section index to be rendered above the auto-generated part |
There are a few more front matter fields available specifically for blog posts:
Field | Description |
---|---|
publishdate | Date of the post’s original publication |
last_update | Date when the post last received a major revision |
attribution | Optional name of the post’s author |
twitter | Optional Twitter handle of the post’s author |
Adding images
Put image files in the same directory as your markdown file. The preferred image format is SVG. Within markdown, use the following sequence to add the image:
{{< image width="75%" ratio="45.34%"
link="./myfile.svg"
alt="Alternate text to display when the image can't be loaded"
title="A tooltip displayed when hovering over the image"
caption="A caption displayed under the image"
>}}
The link
and caption
values are required, all other values are optional.
If the title
value isn’t supplied, it’ll default to the same as caption
. If the alt
value is not supplied, it’ll
default to title
or if that’s not defined, to caption
.
width
represents the percentage of space used by the image
relative to the surrounding text. If the value is not specified, it
defaults to 100%.
ratio
represents the ratio of the image height compared to the image width. This
value is calculated automatically for any local image content, but must be calculated
manually when referencing external image content.
In that case, ratio
should be set to (image height / image width) * 100.
Adding icons
You can embed some common icons in your content using:
{{< warning_icon >}}
{{< idea_icon >}}
{{< checkmark_icon >}}
{{< cancel_icon >}}
{{< tip_icon >}}
which look like , , , and .
Linking to other pages
There are three types of links that can be included in documentation. Each uses a different way to indicate the link target:
Internet Link. You use classic URL syntax, preferably with the HTTPS protocol, to reference files on the Internet:
[see here](https://mysite/myfile.html)
Relative Link. You use relative links that start with a period to reference any content that is at the same level as the current file, or below within the hierarchy of the site:
[see here](./adir/anotherfile.html)
Absolute Link. You use absolute links that start with a
/
to reference content outside of the current hierarchy:[see here](/docs/adir/afile/)
GitHub
There are a few ways to reference files from GitHub:
{{< github_file >}} is how you reference individual files in GitHub such as yaml files. This produces a link to
https://raw.githubusercontent.com/istio/istio*
[liveness]({{< github_file >}}/samples/health-check/liveness-command.yaml)
{{< github_tree >}} is how you reference a directory tree in GitHub. This produces a link to
https://github.com/istio/istio/tree*
[httpbin]({{< github_tree >}}/samples/httpbin)
{{< github_blob >}} is how you reference a file in GitHub sources. This produces a link to
https://github.com/istio/istio/blob*
[RawVM MySQL]({{< github_blob >}}/samples/rawvm/README.md)
The above annotations yield links to the appropriate branch in GitHub, relative to the branch that the
documentation is currently targeting. If you need to manually construct a URL, you can use the sequence {{< source_branch_name >}}
to get the name of the currently targeted branch.
Version information
You can obtain the current Istio version described by the web site using either of {{< istio_version >}}
or
{{< istio_full_version >}}
which render as 1.1 and 1.1.9 respectively.
{{< source_branch_name >}}
gets expanded to the name of the branch of the istio/istio
GitHub repository that the
web site is targeting. This renders as release-1.1.
Embedding preformatted blocks
You can embed blocks of preformatted content using the text
sequence:
{{< text plain >}}
func HelloWorld() {
fmt.Println("Hello World")
}
{{< /text >}}
The above produces this kind of output:
func HelloWorld() {
fmt.Println("Hello World")
}
You must indicate the syntax of the content in the preformatted block. Above, the block was marked as
being plain
indicating that no syntax coloring should be applied to the block. Consider the same
block, but now annotated with the Go language syntax:
{{< text go >}}
func HelloWorld() {
fmt.Println("Hello World")
}
{{< /text >}}
which renders as:
func HelloWorld() {
fmt.Println("Hello World")
}
Supported syntax are plain
, markdown
, yaml
, json
, java
, javascript
, c
, cpp
, csharp
, go
, html
, protobuf
,
perl
, docker
, and bash
.
Command-lines
When showing one or more bash command-lines, you start each command-line with a $:
{{< text bash >}}
$ echo "Hello"
{{< /text >}}
which produces:
$ echo "Hello"
You can have as many command-lines as you want, but only one chunk of output is recognized.
{{< text bash >}}
$ echo "Hello" >file.txt
$ cat file.txt
Hello
{{< /text >}}
which yields:
$ echo "Hello" >file.txt
$ cat file.txt
Hello
You can also use line continuation in your command-lines:
{{< text bash >}}
$ echo "Hello" \
>file.txt
$ echo "There" >>file.txt
$ cat file.txt
Hello
There
{{< /text >}}
which looks like:
$ echo "Hello" \
>file.txt
$ echo "There" >>file.txt
$ cat file.txt
Hello
There
By default, the output section is handled using the plain
syntax. If the output uses a well-known
syntax, you can specify it and get proper coloring for it. This is particularly common for YAML or JSON output:
{{< text bash json >}}
$ kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep \"instance\":\"newlog.logentry.istio-system\"
{"level":"warn","ts":"2017-09-21T04:33:31.249Z","instance":"newlog.logentry.istio-system","destination":"details","latency":"6.848ms","responseCode":200,"responseSize":178,"source":"productpage","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.291Z","instance":"newlog.logentry.istio-system","destination":"ratings","latency":"6.753ms","responseCode":200,"responseSize":48,"source":"reviews","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.263Z","instance":"newlog.logentry.istio-system","destination":"reviews","latency":"39.848ms","responseCode":200,"responseSize":379,"source":"productpage","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.239Z","instance":"newlog.logentry.istio-system","destination":"productpage","latency":"67.675ms","responseCode":200,"responseSize":5599,"source":"ingress.istio-system.svc.cluster.local","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.233Z","instance":"newlog.logentry.istio-system","destination":"ingress.istio-system.svc.cluster.local","latency":"74.47ms","responseCode":200,"responseSize":5599,"source":"unknown","user":"unknown"}
{{< /text >}}
which gives:
$ kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep \"instance\":\"newlog.logentry.istio-system\"
{"level":"warn","ts":"2017-09-21T04:33:31.249Z","instance":"newlog.logentry.istio-system","destination":"details","latency":"6.848ms","responseCode":200,"responseSize":178,"source":"productpage","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.291Z","instance":"newlog.logentry.istio-system","destination":"ratings","latency":"6.753ms","responseCode":200,"responseSize":48,"source":"reviews","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.263Z","instance":"newlog.logentry.istio-system","destination":"reviews","latency":"39.848ms","responseCode":200,"responseSize":379,"source":"productpage","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.239Z","instance":"newlog.logentry.istio-system","destination":"productpage","latency":"67.675ms","responseCode":200,"responseSize":5599,"source":"ingress.istio-system.svc.cluster.local","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.233Z","instance":"newlog.logentry.istio-system","destination":"ingress.istio-system.svc.cluster.local","latency":"74.47ms","responseCode":200,"responseSize":5599,"source":"unknown","user":"unknown"}
Expanded form
To use the more advanced features for preformatted content which are described in the following sections, you must use the
extended form of the text
sequence rather than the simplified form shown so far. The expanded form uses normal HTML attributes:
{{< text syntax="bash" outputis="json" >}}
$ kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath='{.items[0].metadata.name}') mixer | grep \"instance\":\"newlog.logentry.istio-system\"
{"level":"warn","ts":"2017-09-21T04:33:31.249Z","instance":"newlog.logentry.istio-system","destination":"details","latency":"6.848ms","responseCode":200,"responseSize":178,"source":"productpage","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.291Z","instance":"newlog.logentry.istio-system","destination":"ratings","latency":"6.753ms","responseCode":200,"responseSize":48,"source":"reviews","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.263Z","instance":"newlog.logentry.istio-system","destination":"reviews","latency":"39.848ms","responseCode":200,"responseSize":379,"source":"productpage","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.239Z","instance":"newlog.logentry.istio-system","destination":"productpage","latency":"67.675ms","responseCode":200,"responseSize":5599,"source":"ingress.istio-system.svc.cluster.local","user":"unknown"}
{"level":"warn","ts":"2017-09-21T04:33:31.233Z","instance":"newlog.logentry.istio-system","destination":"ingress.istio-system.svc.cluster.local","latency":"74.47ms","responseCode":200,"responseSize":5599,"source":"unknown","user":"unknown"}
{{< /text >}}
The available attributes are:
Attribute | Description |
---|---|
file | The path of a file to show in the preformatted block. |
url | The URL of a document to show in the preformatted block. |
syntax | The syntax of the preformatted block. |
outputis | When the syntax is bash , this specifies the command output’s syntax. |
downloadas | The default file name used when the user downloads the preformatted block. |
expandlinks | Whether or not to expand GitHub file references in the preformatted block. |
snippet | The name of the snippet of content to extract from the preformatted block. |
Inline vs. imported content
So far, you’ve seen examples of inline preformatted content but it’s also possible to import content, either
from a file in the documentation repository or from an arbitrary URL on the Internet. For this, you use the
text_import
sequence.
You can use text_import
with the file
attribute to reference a file within the documentation repository:
{{< text_import file="test/snippet_example.txt" syntax="plain" >}}
which renders as:
BEFORE
# $snippet SNIP1
This is chunk 1
on two lines
# $endsnippet
# $snippet SNIP2
This is chunk 2
# $endsnippet
# $snippet SNIP1
This is chunk 3
# $endsnippet
AFTER
You can dynamically pull in content from the Internet in a similar way, but using the url
attribute instead of the
file
attribute. Here’s the same file, but retrieved from a URL dynamically rather than being baked into the
HTML statically:
{{< text_import url="https://raw.githubusercontent.com/istio/istio.io/master/test/snippet_example.txt" syntax="plain" >}}
which produces the following result:
If the file is from a different origin site, CORS should be enabled on that site. Note that the
GitHub raw content site (raw.githubusercontent.com
) may be used here.
Download name
You can control the name that the browser
uses when the user chooses to download the preformatted content by using the downloadas
attribute. For example:
{{< text syntax="go" downloadas="hello.go" >}}
func HelloWorld() {
fmt.Println("Hello World")
}
{{< /text >}}
If you don’t specify a download name, then it is derived automatically based on the title of the current page for inline content, or from the name of the file or URL for imported content.
Links to GitHub files
If your preformatted content references a file from Istio’s GitHub repository, you can surround the relative path name of the file with a pair of @ symbols. These indicate that the path should be rendered as a link to the file from the current branch in GitHub. For example:
{{< text bash >}}
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-reviews-v3.yaml@
{{< /text >}}
Which renders as:
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-reviews-v3.yaml@
If your preformatted content happens to use @ symbols for something else, you can turn off link expansion using the
expandlinks
attribute:
{{< text syntax="bash" expandlinks="false" >}}
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-reviews-v3.yaml@
{{< /text >}}
Snippets
When using imported content, you can control which parts of the content to render using named snippets, which represent portions
of a file. You declare snippets in a file using the $snippets
annotation with a paired $endsnippet
annotation. The content
between the two annotations represents the snippet.
For example, you could have a text file that looks like this:
BEFORE
# $snippet SNIP1
This is chunk 1
on two lines
# $endsnippet
# $snippet SNIP2
This is chunk 2
# $endsnippet
# $snippet SNIP1
This is chunk 3
# $endsnippet
AFTER
and in your markdown file, you can then reference a particular snippet with the snippet
attribute such as:
{{< text_import file="test/snippet_example.txt" syntax="plain" snippet="SNIP1" >}}
which renders as:
This is chunk 1
on two lines
Glossary terms
When first introducing a specialized Istio term in a page, it is desirable to annotate the terms as being in the glossary. This will produce special rendering inviting the user to click on the term in order to get a pop-up with the definition.
Mixer uses {{<gloss>}}adapters{{</gloss>}} to interface to backends.
which looks like:
Mixer uses adapters to interface to backends.
If the term displayed on the page doesn’t exactly match the entry in the glossary, you can specify a substitution:
Mixer uses an {{<gloss adapters>}}adapter{{</gloss>}} to interface to a backend.
which looks like:
Mixer uses an adapter to interface to a backend.
So even though the glossary entry is for adapters, the singular form of adapter can be used in the text.
Callouts
You can bring special attention to blocks of content by highlighting warnings, ideas, tips, and quotes:
{{< warning >}}
This is an important warning
{{< /warning >}}
{{< idea >}}
This is a great idea
{{< /idea >}}
{{< tip >}}
This is a useful tip from an expert
{{< /tip >}}
{{< quote >}}
This is a quote from somewhere
{{< /quote >}}
which looks like:
Embedding boilerplate text
You can embed common boilerplate text into any markdown output using the boilerplate
sequence:
{{< boilerplate example >}}
which results in:
This is some boilerplate markdown text.
A sample nested text block.
You supply the name of a boilerplate file to insert at the current location. Available boilerplates are
located in the boilerplates
directory. Boilerplates are just
normal markdown files.
Using tabs
If you have some content to display in a variety of formats, it is convenient to use a tab set and display each
format in a different tab. To insert tabbed content, you use a combination of tabset
and tabs
annotations:
{{< tabset cookie-name="platform" >}}
{{< tab name="One" cookie-value="one" >}}
ONE
{{< /tab >}}
{{< tab name="Two" cookie-value="two" >}}
TWO
{{< /tab >}}
{{< tab name="Three" cookie-value="three" >}}
THREE
{{< /tab >}}
{{< /tabset >}}
which produces the following output:
The name
attribute of each tab contains the text to display for the tab. The content of the tab can be almost any normal markdown.
The optional cookie-name
and cookie-value
attributes allow the tab setting to be sticky across visits to the page. As the user
selects a tab, the cookie will be automatically saved with the given name and value. If multiple tab sets use the same cookie name
and values, their setting will be automatically synchronized across pages. This is particularly useful when there are many tab sets
in the site that hold the same types of formats.
For example, if many tab sets are used to represent a choice between GCP
, BlueMix
and AWS
, they can all use a cookie name of environment
and values of
gcp
, bluemix
, and aws
. When a user selects a tab in one page, the equivalent tab will automatically be selected in any other tab set.
Limitations
You can use almost any markdown in a tab, except for the following:
No headers. Headers in a tab will appear in the table of contents and yet clicking on the entry in the table of contents will not automatically select the tab.
No nested tab sets. Don’t try it, it’s horrible.
Renaming, moving, or deleting pages
If you move pages around or delete them completely, you should make sure existing links users may have to those pages continue to work. You do this by adding aliases which will cause the user to be redirected automatically from the old URL to a new URL.
In the page that is the target of the redirect (where you’d like users to land), you simply add the following to the front-matter:
aliases:
- <path>
For example
---
title: Frequently Asked Questions
description: Questions Asked Frequently.
weight: 12
aliases:
- /faq
---
With the above in a page saved as help/faq.md
, the user will be able to access the page by going
to istio.io/help/faq/
as normal, as well as istio.io/faq/
.
You can also add many redirects like so:
---
title: Frequently Asked Questions
description: Questions Asked Frequently.
weight: 12
aliases:
- /faq
- /faq2
- /faq3
---
Building and testing the site
Once you’ve edited some content files, you’ll want to build the site in order to test your changes. We use Hugo to generate our sites. To build and test the site locally, we use a Docker image that contains Hugo. To build and serve the site, simply go to the root of the tree and do:
$ make serve
This will build the site and start a web server hosting the site. You can then connect to the web server
at http://localhost:1313
.
To make and serve the site from a remote server, override ISTIO_SERVE_DOMAIN
as follows with the IP address
or DNS Domain of the server as follows:
$ make ISTIO_SERVE_DOMAIN=192.168.7.105 serve
This will build the site and start a web server hosting the site. You can then connect to the web server
at http://192.168.7.105:1313
.
All English content for the site is located in the content
directory, as well as in sibling translated
directories such as content_zh
.
Linting
We use linters to ensure some base quality to the site’s content. These linters must run without complaining before you can submit your changes into the repository. The linters check:
HTML proofing, which ensures all your links are valid along with other checks.
Spell checking.
Style checking, which makes sure your markdown files comply with our common style rules.
You can run these linters locally using:
$ make lint
If you get spelling errors, you have three choices to address each:
It’s a real typo, so fix your markdown.
It’s a command/field/symbol name, so stick some
backticks
around it.It’s really valid, so go add the word to the
.spelling
file which is at the root of the repo.
If you’re having trouble with the link checker due to poor Internet connectivity, you can set any value to an environment variable named
INTERNAL_ONLY
to prevent the linter from checking external links:
$ make INTERNAL_ONLY=True lint