├── .gitignore
├── .gitreview
├── .stestr.conf
├── .zuul.yaml
├── LICENSE
├── README.rst
├── doc
└── source
│ ├── conf.py
│ ├── guidedreview.rst
│ ├── guidelines
│ ├── index.rst
│ ├── liaisons.json
│ ├── liaisons.rst
│ ├── process.rst
│ └── template.rst
├── guidelines
├── api-docs.rst
├── api_interoperability.rst
├── compatibility.rst
├── consuming-catalog.rst
├── consuming-catalog
│ ├── authority.rst
│ ├── endpoint.rst
│ └── version-discovery.rst
├── counting.rst
├── discoverability.rst
├── dns-sd.rst
├── errors-example.json
├── errors-schema.json
├── errors.rst
├── etags.rst
├── evaluating_api_changes.rst
├── extensions.rst
├── general.rst
├── headers.rst
├── http.rst
├── http
│ ├── caching.rst
│ ├── methods.rst
│ └── response-codes.rst
├── links.rst
├── metadata.rst
├── microversion-errors-example.json
├── microversion_specification.rst
├── naming.rst
├── pagination_filter_sort.rst
├── representation_structure.rst
├── sdk-exposing-microversions.rst
├── tags.rst
├── terms.rst
├── testing.rst
├── time.rst
├── uri.rst
├── version-discovery-schema.json
├── version-information-schema.json
└── versioned-discovery-schema.json
├── requirements.txt
├── setup.cfg
├── setup.py
├── tests
├── __init__.py
└── test_titles.py
├── tools
└── add-reviewers.py
└── tox.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | AUTHORS
2 | ChangeLog
3 | build
4 | .tox
5 | .venv
6 | *.egg*
7 | *.swp
8 | *.swo
9 | *.pyc
10 | .stestr/
11 |
--------------------------------------------------------------------------------
/.gitreview:
--------------------------------------------------------------------------------
1 | [gerrit]
2 | host=review.opendev.org
3 | port=29418
4 | project=openstack/api-sig.git
5 |
--------------------------------------------------------------------------------
/.stestr.conf:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | test_path=./tests
3 | top_dir=./
4 |
5 |
--------------------------------------------------------------------------------
/.zuul.yaml:
--------------------------------------------------------------------------------
1 | - project:
2 | templates:
3 | - openstack-specs-jobs
4 | check:
5 | jobs:
6 | - openstack-tox-py37
7 | - openstack-tox-linters
8 | gate:
9 | jobs:
10 | - openstack-tox-py37
11 | - openstack-tox-linters
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This work is licensed under a Creative Commons Attribution 3.0 Unported License.
2 |
3 | http://creativecommons.org/licenses/by/3.0/legalcode
4 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ======
2 | README
3 | ======
4 |
5 | Openstack API Special Interest Group documents
6 | ----------------------------------------------
7 |
8 | This repository contains documents from the OpenStack API Special Interest
9 | Group, including guidelines and proposed rules concerning API consistency,
10 | naming conventions, and best practice recommendations. The published
11 | information can be found at `specs.openstack.org
12 | `_.
13 |
14 | Interested in contributing to the API conversations? Simply clone this
15 | repository and follow the `OpenStack code and review submission
16 | processes `_
17 | and the `process document
18 | `_.
19 |
--------------------------------------------------------------------------------
/doc/source/conf.py:
--------------------------------------------------------------------------------
1 | # Tempest documentation build configuration file, created by
2 | # sphinx-quickstart on Tue May 21 17:43:32 2013.
3 | #
4 | # This file is execfile()d with the current directory set to its containing dir.
5 | #
6 | # Note that not all possible configuration values are present in this
7 | # autogenerated file.
8 | #
9 | # All configuration values have a default; values that are commented out
10 | # serve to show the default.
11 |
12 | import datetime
13 | import sys
14 | import os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = ['sphinx.ext.autodoc',
29 | 'sphinx.ext.todo',
30 | 'sphinx.ext.viewcode',
31 | 'openstackdocstheme',
32 | 'yasfb',
33 | ]
34 |
35 | # Feed configuration for yasfb
36 | feed_base_url = 'https://specs.openstack.org/openstack/api-sig'
37 | feed_author = 'OpenStack API Special Interest Group'
38 |
39 | todo_include_todos = True
40 |
41 | # Add any paths that contain templates here, relative to this directory.
42 | templates_path = ['_templates']
43 |
44 | # The suffix of source filenames.
45 | source_suffix = '.rst'
46 |
47 | # The encoding of source files.
48 | #source_encoding = 'utf-8-sig'
49 |
50 | # The master toctree document.
51 | master_doc = 'index'
52 |
53 | # General information about the project.
54 | project = 'API Special Interest Group'
55 | copyright = '%s, OpenStack API Special Interest Group Team' % datetime.date.today().year
56 |
57 | # openstackdocstheme options
58 | openstackdocs_repo_name = 'openstack/api-sig'
59 | openstackdocs_auto_name = False
60 |
61 | # The language for content autogenerated by Sphinx. Refer to documentation
62 | # for a list of supported languages.
63 | #language = None
64 |
65 | # There are two options for replacing |today|: either, you set today to some
66 | # non-false value, then it is used:
67 | #today = ''
68 | # Else, today_fmt is used as the format for a strftime call.
69 | #today_fmt = '%B %d, %Y'
70 |
71 | # List of patterns, relative to source directory, that match files and
72 | # directories to ignore when looking for source files.
73 | exclude_patterns = [
74 | '_build',
75 | ]
76 |
77 | # The reST default role (used for this markup: `text`) to use for all documents.
78 | #default_role = None
79 |
80 | # If true, '()' will be appended to :func: etc. cross-reference text.
81 | #add_function_parentheses = True
82 |
83 | # If true, the current module name will be prepended to all description
84 | # unit titles (such as .. function::).
85 | add_module_names = False
86 |
87 | # If true, sectionauthor and moduleauthor directives will be shown in the
88 | # output. They are ignored by default.
89 | show_authors = False
90 |
91 | # The name of the Pygments (syntax highlighting) style to use.
92 | pygments_style = 'native'
93 |
94 | # A list of ignored prefixes for module index sorting.
95 | modindex_common_prefix = []
96 |
97 | # -- Options for man page output ----------------------------------------------
98 | man_pages = []
99 |
100 | # -- Options for HTML output ---------------------------------------------------
101 |
102 | # The theme to use for HTML and HTML Help pages. See the documentation for
103 | # a list of builtin themes.
104 | html_theme = 'openstackdocs'
105 |
106 | # Theme options are theme-specific and customize the look and feel of a theme
107 | # further. For a list of options available for each theme, see the
108 | # documentation.
109 | #html_theme_options = {}
110 |
111 | # Add any paths that contain custom themes here, relative to this directory.
112 | #html_theme_path = []
113 |
114 | # The name for this set of Sphinx documents. If None, it defaults to
115 | # " v documentation".
116 | #html_title = None
117 |
118 | # A shorter title for the navigation bar. Default is the same as html_title.
119 | #html_short_title = None
120 |
121 | # The name of an image file (relative to this directory) to place at the top
122 | # of the sidebar.
123 | #html_logo = None
124 |
125 | # The name of an image file (within the static path) to use as favicon of the
126 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
127 | # pixels large.
128 | #html_favicon = None
129 |
130 | # If true, SmartyPants will be used to convert quotes and dashes to
131 | # typographically correct entities.
132 | #html_use_smartypants = True
133 |
134 | # Custom sidebar templates, maps document names to template names.
135 | #html_sidebars = {}
136 |
137 | # Additional templates that should be rendered to pages, maps page names to
138 | # template names.
139 | #html_additional_pages = {}
140 |
141 | # If false, no module index is generated.
142 | html_domain_indices = False
143 |
144 | # If false, no index is generated.
145 | html_use_index = False
146 |
147 | # If true, the index is split into individual pages for each letter.
148 | #html_split_index = False
149 |
150 | # If true, links to the reST sources are added to the pages.
151 | #html_show_sourcelink = True
152 |
153 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
154 | #html_show_sphinx = True
155 |
156 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
157 | #html_show_copyright = True
158 |
159 | # If true, an OpenSearch description file will be output, and all pages will
160 | # contain a tag referring to it. The value of this option must be the
161 | # base URL from which the finished HTML is served.
162 | #html_use_opensearch = ''
163 |
164 | # This is the file name suffix for HTML files (e.g. ".xhtml").
165 | #html_file_suffix = None
166 |
167 | # Output file base name for HTML help builder.
168 | htmlhelp_basename = 'API-Sig-Doc'
169 |
170 |
171 | # -- Options for LaTeX output --------------------------------------------------
172 |
173 | latex_elements = {
174 | # The paper size ('letterpaper' or 'a4paper').
175 | #'papersize': 'letterpaper',
176 |
177 | # The font size ('10pt', '11pt' or '12pt').
178 | #'pointsize': '10pt',
179 |
180 | # Additional stuff for the LaTeX preamble.
181 | #'preamble': '',
182 | }
183 |
184 | # Grouping the document tree into LaTeX files. List of tuples
185 | # (source start file, target name, title, author, documentclass [howto/manual]).
186 | latex_documents = [
187 | ('index', 'api-sig.tex', 'API Special Interest Group',
188 | 'OpenStack API Special Interest Group Team', 'manual'),
189 | ]
190 |
191 | # The name of an image file (relative to this directory) to place at the top of
192 | # the title page.
193 | #latex_logo = None
194 |
195 | # For "manual" documents, if this is true, then toplevel headings are parts,
196 | # not chapters.
197 | #latex_use_parts = False
198 |
199 | # If true, show page references after internal links.
200 | #latex_show_pagerefs = False
201 |
202 | # If true, show URL addresses after external links.
203 | #latex_show_urls = False
204 |
205 | # Documents to append as an appendix to all manuals.
206 | #latex_appendices = []
207 |
208 | # If false, no module index is generated.
209 | #latex_domain_indices = True
210 |
211 | # -- Options for Texinfo output ------------------------------------------------
212 |
213 | # Grouping the document tree into Texinfo files. List of tuples
214 | # (source start file, target name, title, author,
215 | # dir menu entry, description, category)
216 | texinfo_documents = [
217 | ('index', 'api-sig', 'API Special Interest Group',
218 | 'OpenStack API Special Interest Group Team', 'nova-specs', 'Guidelines for OpenStack APIs.',
219 | 'Miscellaneous'),
220 | ]
221 |
222 | # Documents to append as an appendix to all manuals.
223 | #texinfo_appendices = []
224 |
225 | # If false, no module index is generated.
226 | #texinfo_domain_indices = True
227 |
228 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
229 | #texinfo_show_urls = 'footnote'
230 |
231 |
232 | # -- Options for Epub output ---------------------------------------------------
233 |
234 | # Bibliographic Dublin Core info.
235 | epub_title = 'API Special Interest Group'
236 | epub_author = 'OpenStack API Special Interest Group Team'
237 | epub_publisher = 'OpenStack API Special Interest Group Team'
238 | epub_copyright = '2014, OpenStack API Special Interest Group Team'
239 |
240 | # The language of the text. It defaults to the language option
241 | # or en if the language is not set.
242 | #epub_language = ''
243 |
244 | # The scheme of the identifier. Typical schemes are ISBN or URL.
245 | #epub_scheme = ''
246 |
247 | # The unique identifier of the text. This can be a ISBN number
248 | # or the project homepage.
249 | #epub_identifier = ''
250 |
251 | # A unique identification for the text.
252 | #epub_uid = ''
253 |
254 | # A tuple containing the cover image and cover page html template filenames.
255 | #epub_cover = ()
256 |
257 | # HTML files that should be inserted before the pages created by sphinx.
258 | # The format is a list of tuples containing the path and title.
259 | #epub_pre_files = []
260 |
261 | # HTML files shat should be inserted after the pages created by sphinx.
262 | # The format is a list of tuples containing the path and title.
263 | #epub_post_files = []
264 |
265 | # A list of files that should not be packed into the epub file.
266 | #epub_exclude_files = []
267 |
268 | # The depth of the table of contents in toc.ncx.
269 | #epub_tocdepth = 3
270 |
271 | # Allow duplicate toc entries.
272 | #epub_tocdup = True
273 |
--------------------------------------------------------------------------------
/doc/source/guidedreview.rst:
--------------------------------------------------------------------------------
1 | =====================
2 | Guided Review Process
3 | =====================
4 |
5 | The API Special Interest Group would like to increase the tools available to
6 | OpenStack project teams by defining a process and venue for conducting live
7 | face-to-face reviews. At a high level, these reviews are focused on the
8 | question "does my project align with the guidelines?" and are intended to be
9 | hosted by the API Special Interest Group at the PTG meetups.
10 |
11 | On a more concrete level, the API Special Interest Group expects that reviews
12 | which are directed towards specific areas, or problems within, an API will
13 | garner the best results (e.g. "Is using a PUT request for updating these
14 | resources appropriate?", "We are using GET requests to start server actions, is
15 | there a better alternative?").
16 |
17 | Ideal Candidates for Review
18 | ---------------------------
19 |
20 | It may be difficult if not impossible to review the entire API of a project in
21 | a session at the PTG. So here are some suggestions for selecting discussion
22 | topics:
23 |
24 | Is there a part of your API that has generated some disagreement on your
25 | team? If so, that would be an excellent subject for discussion at the PTG.
26 |
27 | Are you unhappy with how some of your current API works? We could brainstorm
28 | ideas about making it more consistent and usable.
29 |
30 | Are you creating a new API? If you're starting a new project, or creating a
31 | new major version for your project, we can discuss what would be the most
32 | effective ways to approach it.
33 |
34 | How do I get my project reviewed?
35 | ---------------------------------
36 |
37 | Here are some things you should prepare before approaching the API working
38 | group for a live review of your project:
39 |
40 | 1. Pick an area of your API to focus on (e.g. a specific resource type, an
41 | interaction that is troublesome or endpoints that could use clarification).
42 |
43 | 2. Prepare a document describing the API in question, this could be free-form
44 | or something more formal like an OpenAPI specification. This does not need
45 | to be an expansive document, but should help drive the review conversation
46 | and provide a reference for the reviewers to understand the flow of the API
47 | in question.
48 |
49 | Then just show up to an API Special Interest Group face-to-face session with
50 | your materials ready. Sending an email ahead of time will help to ensure that
51 | the group is ready for your review but is not strictly necessary.
52 |
53 | What should I expect from my review?
54 | ------------------------------------
55 |
56 | The answer to this will vary depending on the nature of your request to the
57 | group. You should expect to have clarification on the basic question of how
58 | close your API follows the API-SIG's guidelines. But, the depth of your review
59 | will depend entirely on how big a request is made of the review group.
60 |
61 | The preparations that a project team makes before the review process will have
62 | the largest effect on the expected outcome. Teams that are more specific and
63 | focused with their requests to the review group will most likely harvest the
64 | greatest fruits from this process.
65 |
66 | When considering approaching the API-SIG for a review, we encourage projects to
67 | identify areas of their APIs that could use clarification or have been
68 | problematic to the team. Project teams that have reviewed the API guidelines
69 | and have questions about specific areas of interest will find that their
70 | reviews will be more productive than open-ended questions which cover large
71 | portions of their API.
72 |
73 | After a review is completed, the API Special Interest Group will archive the
74 | general details of the review (e.g. "Team requested a review of API
75 | features and . It was agreed that actions , and are the best
76 | path forward") and any artifacts that are generated during the process. This
77 | archive will exist in the same repository as the guidelines under a separate
78 | heading for reviews.
79 |
80 | Example Review
81 | --------------
82 |
83 | **TODO**
84 |
--------------------------------------------------------------------------------
/doc/source/guidelines:
--------------------------------------------------------------------------------
1 | ../../guidelines
--------------------------------------------------------------------------------
/doc/source/index.rst:
--------------------------------------------------------------------------------
1 | .. api-wg documentation master file
2 |
3 | ====================================
4 | OpenStack API Special Interest Group
5 | ====================================
6 |
7 | Mission Statement
8 | -----------------
9 |
10 | To improve the developer experience of API users by converging the OpenStack
11 | API to a consistent and pragmatic RESTful design. The working group creates
12 | guidelines that all OpenStack projects should follow for new development, and
13 | promotes convergence of new APIs and future versions of existing APIs.
14 |
15 | Preamble
16 | --------
17 |
18 | This document contains the guidelines and rules for OpenStack project
19 | APIs including guidelines and proposed rules concerning API consistency, naming
20 | conventions, and best practice recommendations.
21 |
22 | If you would like to connect with the API Special Interest Group, visit the
23 | wiki at: https://wiki.openstack.org/wiki/API_Special_Interest_Group
24 |
25 | If you are interested in contributing to this document, the git repository is
26 | available at: http://opendev.org/openstack/api-sig/
27 |
28 | OpenStack code and review submission processes are described at:
29 | http://docs.openstack.org/infra/manual/developers.html
30 |
31 |
32 | .. warning::
33 | These documents from the API Special Interest Group are primarily focused
34 | on providing advice and guidelines for JSON-based APIs. While other
35 | representations have their place in the OpenStack ecosystem, they present
36 | such a diversity of challenges and edge cases, especially with large and/or
37 | binary request and response bodies, that it is impossible to provide
38 | guidance that is complete.
39 |
40 | .. note::
41 | Where this guidance is incomplete or ambiguous, refer to the HTTP
42 | RFCs—:rfc:`7230`, :rfc:`7231`, :rfc:`7232`, :rfc:`7233`, :rfc:`7234`, and
43 | :rfc:`7235`—as the ultimate authority. For advice on effectively using
44 | HTTP in APIs see `Building Protocols with
45 | HTTP `_.
46 |
47 | Guidelines
48 | ----------
49 |
50 | The following topics are related to the working group and its processes:
51 |
52 | .. toctree::
53 | :glob:
54 | :maxdepth: 1
55 |
56 | process
57 | template
58 | liaisons
59 | guidedreview
60 |
61 | These topics are the API guidance approved by the OpenStack community
62 | and published by the working group:
63 |
64 | .. toctree::
65 | :glob:
66 | :maxdepth: 1
67 |
68 | guidelines/*
69 |
--------------------------------------------------------------------------------
/doc/source/liaisons.json:
--------------------------------------------------------------------------------
1 | {
2 | "liaisons": [
3 | {
4 | "project": "Barbican",
5 | "name": "Douglas Mendizábal",
6 | "email": "douglas.mendizabal@rackspace.com",
7 | "nick": "redrobot"
8 | },
9 | {
10 | "project": "Ceilometer",
11 | "name": "Chris Dent",
12 | "email": "cdent+os@anticedent.org",
13 | "nick": "cdent"
14 | },
15 | {
16 | "project": "Cinder",
17 | "name": "Scott DAngelo",
18 | "email": "scott.dangelo@gmail.com",
19 | "nick": "scottda"
20 | },
21 | {
22 | "project": "Congress",
23 | "name": "Masahito Muroi",
24 | "email": "muroi.masahito@lab.ntt.co.jp",
25 | "nick": "masahito"
26 | },
27 | {
28 | "project": "Designate",
29 | "name": "",
30 | "email": "",
31 | "nick": ""
32 | },
33 | {
34 | "project": "Glance",
35 | "name": "Nikhil Komawar",
36 | "email": "nik.komawar@gmail.com",
37 | "nick": "nikhil_k"
38 | },
39 | {
40 | "project": "Heat",
41 | "name": "Rico Lin",
42 | "email": "ricolin@ricolky.com",
43 | "nick": "ricolin"
44 | },
45 | {
46 | "project": "Horizon",
47 | "name": "Cindy Lu",
48 | "email": "clu@us.ibm.com",
49 | "nick": "clu_"
50 | },
51 | {
52 | "project": "Ironic",
53 | "name": "Vladyslav Drok",
54 | "email": "vdrok@mirantis.com",
55 | "nick": "vdrok"
56 | },
57 | {
58 | "project": "Keystone",
59 | "name": "Colleen Murphy",
60 | "email": "colleen@gazlene.net",
61 | "nick": "cmurphy"
62 | },
63 | {
64 | "project": "MagnetoDB",
65 | "name": "Ilya Sviridov",
66 | "email": "sviridov.ilya@gmail.com",
67 | "nick": "isviridov"
68 | },
69 | {
70 | "project": "Magnum",
71 | "name": "Eli Qiao",
72 | "email": "liyong.qiao@intel.com",
73 | "nick": "eliqiao"
74 | },
75 | {
76 | "project": "Magnum",
77 | "name": "Hua Wang",
78 | "email": "wanghua.humble@gmail.com",
79 | "nick": "wanghua"
80 | },
81 | {
82 | "project": "Manila",
83 | "name": "Goutham Pacha Ravi",
84 | "email": "Goutham.PachaRavi@netapp.com",
85 | "nick": "gouthamr"
86 | },
87 | {
88 | "project": "Mistral",
89 | "name": "Renat Akhmerov",
90 | "email": "renat.akhmerov@gmail.com",
91 | "nick": "rakhmerov"
92 | },
93 | {
94 | "project": "Murano",
95 | "name": "Nikolay Starodubtsev",
96 | "email": "nstarodubstev@mirantis.com",
97 | "nick": "Nikolay_St"
98 | },
99 | {
100 | "project": "Neutron",
101 | "name": "Akihiro Motoki",
102 | "email": "amotoki@gmail.com",
103 | "nick": "amotoki"
104 | },
105 | {
106 | "project": "Nova",
107 | "name": "Alex Xu",
108 | "email": "soulxu@gmail.com",
109 | "nick": "alex_xu"
110 | },
111 | {
112 | "project": "Placement",
113 | "name": "Ed Leafe",
114 | "email": "ed@leafe.com",
115 | "nick": "edleafe"
116 | },
117 | {
118 | "project": "Rally",
119 | "name": "",
120 | "email": "",
121 | "nick": ""
122 | },
123 | {
124 | "project": "Sahara",
125 | "name": "Michael McCune",
126 | "email": "msm@redhat.com",
127 | "nick": "elmiko"
128 | },
129 | {
130 | "project": "Senlin",
131 | "name": "Qiming Teng",
132 | "email": "tengqim@linux.vnet.ibm.com",
133 | "nick": "Qiming"
134 | },
135 | {
136 | "project": "Swift",
137 | "name": "John Dickinson",
138 | "email": "me@not.mn",
139 | "nick": "notmyname"
140 | },
141 | {
142 | "project": "Trove",
143 | "name": "Peter Stachowski",
144 | "email": "peter@tesora.com",
145 | "nick": "peterstac"
146 | },
147 | {
148 | "project": "Trove",
149 | "name": "Amrith Kumar",
150 | "email": "amrith.kumar@gmail.com",
151 | "nick": "amrith"
152 | },
153 | {
154 | "project": "Tripleo",
155 | "name": "",
156 | "email": "",
157 | "nick": ""
158 | },
159 | {
160 | "project": "Zaqar",
161 | "name": "Fei Long Wang",
162 | "email": "feilong@catalyst.net.nz",
163 | "nick": "flwang"
164 | }
165 | ]
166 | }
167 |
--------------------------------------------------------------------------------
/doc/source/liaisons.rst:
--------------------------------------------------------------------------------
1 | Cross-Project Liaisons
2 | ======================
3 |
4 | Description
5 | -----------
6 |
7 | The API Special Interest Group seeks API subject matter experts for each
8 | project to communicate plans for API updates, review API guidelines with their
9 | project's view in mind, and review the API Special Interest Group guidelines as
10 | they are drafted. The Cross-Project Liaison (CPL) should be familiar with the
11 | project's REST API design and future planning for changes to it.
12 |
13 | * The liaison should be the PTL or whomever they delegate to be their
14 | representative
15 | * The liaison is the first line of contact for the API Special Interest Group
16 | team members
17 | * The liaison may further delegate work to other subject matter experts
18 | * The liaison should be aware of and engaged in the API Special Interest Group
19 | `Communication channels
20 | `_
21 | * The Nova team has been very explicit about how they will liaise with the
22 | API Special Interest Group; see the `Responsibilities of Liaisons `_
23 |
24 | Tooling
25 | -------
26 |
27 | To make it easier to engage the liaisons, we have a tool that will add all
28 | current liaisons to an API WG review.
29 |
30 | You can run the tool like so from the base dir of the api-wg repository.
31 |
32 | ::
33 |
34 | $ python3 tools/add-reviewers.py my-gerrit-username 183599
35 | Added 21 reviewers to 183599
36 |
37 | To get help use ``--help``.
38 |
39 | ::
40 |
41 | $ python3 tools/add-reviewers.py --help
42 |
43 | Liaisons
44 | --------
45 |
46 | .. literalinclude:: liaisons.json
47 | :language: json
48 |
--------------------------------------------------------------------------------
/doc/source/process.rst:
--------------------------------------------------------------------------------
1 | =========================================
2 | Process for adding or changing guidelines
3 | =========================================
4 |
5 | This document describes the process we will use before merging a non
6 | trivial changeset in the guidelines directory. A non trivial changeset
7 | is one which is more than a spelling/grammar typo or reformatting
8 | change.
9 |
10 | The guidelines are initially intended to be a group draft document. Our intent
11 | is to move fairly quickly to get published draft guidelines so this process
12 | reflects a preference for efficiency while gathering consensus.
13 |
14 | Review process
15 | --------------
16 |
17 | For trivial changes (as defined above) there is no minimum time that they must
18 | be proposed before merging. They must have at least one +1 vote other than the
19 | approver and no -1. Once this is true a working group core may merge the
20 | change.
21 |
22 | For changes which add a new guideline or make substantial changes to an
23 | existing guideline reaching consensus is an explicit goal. To that end there is
24 | a well defined process to ensure that proposals receive adequate review by the
25 | API Special Interest Group, cross project liaisons, and the OpenStack community
26 | at large.
27 |
28 | In the various stages of the review process (defined below) consensus means the
29 | changeset is in its near final form for at least two working days. Minor
30 | typo/formatting changes do not reset the counter. There must be at least four
31 | +1 votes and no -1 votes unless the concern related to the -1 vote has been
32 | discussed in an API WG meeting. Once the matter has been discussed there should
33 | be no more than 20% of votes cast as -1 votes.
34 |
35 | Discussion on Gerrit should be encouraged as the primary response. When
36 | discussion on IRC (in meetings or otherwise) is required that discussion should
37 | be summarized back to Gerrit.
38 |
39 | That process is:
40 |
41 | 1. API Special Interest Group members should review proposed guideline changes
42 | and reach consensus.
43 |
44 | 2. When consensus is reached within the group the guideline should be marked as
45 | *frozen* and cross project liaisons (CPLs) should be invited to review the
46 | guideline. There is an ``add-reviewers.py`` script to do this. See
47 | :doc:`liaisons` for more information.
48 |
49 | A proposal can be frozen by exactly one core reviewer by setting Code-Review
50 | +2. Only the core reviewer responsible for freezing the proposal should +2
51 | it. All other core reviewers should vote with at most a +1 when reviewing.
52 |
53 | 3. The CPLs have one week to review the proposal. If there are no reviews lazy
54 | consensus is assumed. If there is a -1 review by a CPL that requires an
55 | update to the changeset, it does not reset the 1 week the CPLs have to
56 | review it.
57 |
58 | 4. When there is consensus from the CPLs, the proposal may be merged.
59 |
60 | To avoid opportunities for miscommunication, the core working group member
61 | who froze the changeset should be responsible for merging it. If that core
62 | is unavailable for enough time to cause a delay then the responsibility
63 | falls to one of the others cores.
64 |
65 | An email should be sent to the openstack-dev mailing list containing the
66 | links to all of the guidelines that have recently merged. The finalized
67 | guidelines should be buffered such that a maximum of one announcement email
68 | is sent per week.
69 |
70 | If at any time during this process there is difficulty reaching consensus or an
71 | apparent lack of information or input, additional input should be sought from
72 | the rest of the community. Two ways to do this include (preferring email):
73 |
74 | 1. The openstack-dev mailing list. An email may be sent to the openstack-dev
75 | mailing list with the subject "[all][api] New API Guidelines Ready for Cross
76 | Project Review". The email should contain links to the guidelines that need
77 | additional input.
78 |
79 | 2. The `Cross Project Meeting
80 | `_. An agenda
81 | item should be added to the Cross Project Meeting which indicates need for
82 | discussion. Links to the guidelines that need additional input should be
83 | provided. When this is done an API WG member must attend the meeting to
84 | highlight the agenda item.
85 |
86 | Merged guidelines comprise a draft of the official guidelines. Before an
87 | official version of the guidelines can be released the following has to occur:
88 |
89 | * An 80% (of votes cast) majority vote on the document as a whole with one vote
90 | per OpenStack PTL (or delegate).
91 |
92 | * Reviewed and approved by the TC. The API WG is a delegated group from the TC
93 | so the TC ultimately get final say on what the working group are able to
94 | release.
95 |
96 | Proposing a new guideline
97 | -------------------------
98 |
99 | When proposing a new guideline you should start by using the :doc:`guideline
100 | template ` to generate the basic structure. Copy the ``template.rst``
101 | file to the ``guidelines`` directory with a filename reflecting your new
102 | guideline (for example ``guidelines/errors.rst``), and then follow the
103 | instructions within the template. Once complete you should follow the
104 | `developer workflow`_ and the previously stated review process to have your
105 | guideline accepted.
106 |
107 | .. _developer workflow: http://docs.openstack.org/infra/manual/developers.html
108 |
--------------------------------------------------------------------------------
/doc/source/template.rst:
--------------------------------------------------------------------------------
1 | ..
2 | This work is licensed under a Creative Commons Attribution 3.0 Unported
3 | License.
4 |
5 | http://creativecommons.org/licenses/by/3.0/legalcode
6 |
7 | ..
8 | The title of your guideline should replace the
9 | "Example Guideline Category"
10 |
11 | ==========================
12 | Example Guideline Category
13 | ==========================
14 |
15 | Introduction paragraph -- what does this guideline category address? A single
16 | paragraph of prose that implementors can understand. This paragraph should
17 | describe the intent and scope of the guideline. The title and this first
18 | paragraph should be used as the subject line and body of the commit message
19 | respectively.
20 |
21 | Some notes about using this template:
22 |
23 | * Your guideline should be in ReSTructured text, like this template.
24 |
25 | * Please wrap text at 79 columns.
26 |
27 | * For help with syntax, see http://sphinx-doc.org/rest.html
28 |
29 | * To test out your formatting, build the docs using tox, or see:
30 | http://rst.ninjs.org
31 |
32 | * For help with OpenStack documentation conventions, see
33 | https://wiki.openstack.org/wiki/Documentation/Conventions
34 |
35 | Each file should be a group of related guidelines, such as "HTTP Headers" or
36 | similar. Each guideline gets its own subheader, and within each section there
37 | is an overview/introduction, a guidance section, examples, and references. Not
38 | every guideline will fill in every section. If a section isn't needed for a
39 | particular guideline, delete it if you're **really** sure it's superfluous.
40 |
41 | Guideline Name
42 | --------------
43 |
44 | A detailed guideline that is being suggested. It should also have an
45 | introduction if applicable.
46 |
47 | Guidance
48 | ********
49 |
50 | The actual guidance that the API Special Interest Group would like to provide.
51 |
52 | * The guideline should provide a clear recitation of the actions to be
53 | taken by implementors.
54 |
55 | * Reference to specific technology implementations (for example,
56 | XXX-Y.Z package) should be avoided.
57 |
58 | * External references should be described by annotations with the
59 | links to the source material in the References section. (for example,
60 | please see :rfc:`0000` or this footnote guide [#f1]_)
61 |
62 | Examples
63 | ********
64 |
65 | A series of examples that demonstrate the proper usage of the guideline
66 | being proposed. These examples may include, but are not limited to:
67 |
68 | * JSON objects.
69 |
70 | * HTTP methods demonstrating requests and responses.
71 |
72 | The examples should not include:
73 |
74 | * Code samples designed for implementation.
75 |
76 | References
77 | **********
78 |
79 | References may be provided in cases where they aid in giving a more complete
80 | understanding of the guideline. You are not required to have any references.
81 | Moreover, this guideline should still make sense when your references are
82 | unavailable. Examples of what you could include are:
83 |
84 | * Links to mailing list or IRC discussions
85 |
86 | * Links to notes from a summit session
87 |
88 | * Links to relevant research, if appropriate
89 |
90 | RST supports footnotes in the following format:
91 |
92 | .. rubric:: Footnotes
93 |
94 | .. [#f1] http://sphinx-doc.org/rest.html#footnotes
95 |
--------------------------------------------------------------------------------
/guidelines/api-docs.rst:
--------------------------------------------------------------------------------
1 | API Documentation
2 | =================
3 |
4 | By providing guidelines for API documentation for all OpenStack services,
5 | projects use common tooling, consistent outlines, and study exemplary examples
6 | to meet expectations for API documentation.
7 |
8 | Content
9 | -------
10 |
11 | First you should generate or write the reference information including:
12 |
13 | - Method (GET/PUT/POST/PATCH/HEAD/DELETE)
14 | - Resource (Identified by the URL)
15 | - Request parameters, type and description including whether it
16 | is optional
17 | - Request headers including media-type, content-type, accept, and others
18 | - Response headers (for some APIs)
19 | - Responses including types and description
20 | - Example request body and headers
21 | - Example response body and headers
22 | - Status codes: successful request and error responses
23 | - Resource model: describes data types that can be consumed and produced by
24 | operations.
25 |
26 | The resource model may be created with either a parameters.yaml file or with
27 | the `OpenAPI Definitions object `_
28 | Swagger is governed by the OpenAPI initiative.
29 |
30 | See authoring tools below for more information on writing or generating the
31 | parameters information.
32 |
33 | Also important is the conceptual or narrative information that explains the
34 | REST API and what it provides as a service.
35 |
36 | Documentation should also offer information about the concepts for each
37 | resource, such as server status or volume status.
38 |
39 | Documentation should provide a discussion about the consistency model the
40 | service provides and synchronous or asynchronous behavior for certain methods.
41 |
42 | As guidance, here is a list of topics to ensure you include in your API docs
43 | beyond the reference information.
44 |
45 | * Authentication
46 | * Faults (synchronous and asynchronous)
47 | * Limits (rate limits, absolute limits, calls to find out limits)
48 | * Constraints (min and max for certain values even if chosen by provider)
49 | * Content compression
50 | * Encoding
51 | * Links and references
52 | * Pagination
53 | * Filtering
54 | * Sorting
55 | * Formats (request, response)
56 | * Endpoints, versions and discoverability
57 | * Capabilities and discoverability
58 | * Term definitions
59 | (by adding to the OpenStack glossary sourced in openstack-manuals)
60 | * Status or state values
61 | * Hierarchy information
62 | * Quotas
63 | * Extensions
64 |
65 | These topics should be written in RST and built with either Sphinx or alongside
66 | the generated OpenAPI using the Pecan app described below. The outline itself
67 | is not prescribed since REST APIs vary widely.
68 |
69 | Authoring tools
70 | ---------------
71 |
72 | The API documentation authoring tools are described in the
73 | `Contributor Guide `_.
74 |
75 | Publishing tools
76 | ----------------
77 |
78 | The existing OpenStack infrastructure provides publishing to docs.openstack.org
79 | and developer.openstack.org and specs.openstack.org from RST/Sphinx. In the
80 | nova repo, for example, running `tox -e api-ref` builds Sphinx-based API
81 | reference documentation locally.
82 |
83 | For publishing OpenAPI-based reference, refer to
84 | https://review.opendev.org/#/c/286659/ as an example that can be used in the
85 | project repo.
86 |
--------------------------------------------------------------------------------
/guidelines/api_interoperability.rst:
--------------------------------------------------------------------------------
1 | ..
2 | This work is licensed under a Creative Commons Attribution 3.0 Unported
3 | License.
4 |
5 | http://creativecommons.org/licenses/by/3.0/legalcode
6 |
7 | .. _interoperability:
8 |
9 | =============================
10 | Ensuring API Interoperability
11 | =============================
12 |
13 | The OpenStack mission includes the following goal for OpenStack clouds:
14 | "interoperable between deployments". In the context of HTTP APIs this means
15 | that client code that is written for cloud `A` should also work on cloud
16 | `B` and any other cloud. Because cloud `A` and cloud `B` may be running
17 | different releases of the OpenStack services at any given point in time,
18 | and they may upgrade their services at different points in time, this has
19 | important implications for how API developers add features, fix bugs, and
20 | remove functionality from the service APIs.
21 |
22 | If a service wants to ensure (and they should) that client code is always
23 | interoperable with multiple different OpenStack clouds then:
24 |
25 | * The changes must not violate compatibility nor stability, both of which
26 | are defined in more detail below.
27 |
28 | * Changes in resources and request and response bodies and headers are not
29 | allowed without signalling a version boundary, except in a very small
30 | number of cases (see below).
31 |
32 | .. note:: APIs which have different behaviors and representations based on
33 | the availability of different drivers present significant
34 | challenges for interoperability. Where possible such differences
35 | should be avoided. Where that's not possible, interoperability
36 | should be considered when cloud `A` and cloud `B` have the same
37 | sets of drivers available.
38 |
39 | * Version discovery and selection should default to a baseline; making use of
40 | new features and changes in an API should be opt-in.
41 |
42 | * When functionality is fully removed, this must result in the baseline
43 | being raised. Ideally this should result in the equivalent of a major
44 | version update because stability (backwards compatibility) has been
45 | violated. When a service uses microversions, "major version update" often
46 | means raising the minimum available version.
47 |
48 | * If functionality is going to be removed its removal should follow
49 | standard OpenStack deprecation procedures.
50 |
51 | * A service that wants to deprecate some functionality but maintain
52 | stability is welcome to do so by documenting the functionality as
53 | deprecated but keeping the functionality present in the API.
54 |
55 | The gist here is that any service with a published API that also needs to
56 | make changes to that API (to fix or evolve it) needs to have a mechanism
57 | for versioning. This document does not address versioning, however the
58 | only mechanism in active use in OpenStack that has been demonstrated to
59 | work for the goals described here are :doc:`microversions
60 | `.
61 |
62 | The rest of this document will describe the rules that a service MUST
63 | follow if it wishes to ensure API interoperability. It makes two
64 | assumptions that are important for understanding the compromises this document
65 | makes:
66 |
67 | * Change in APIs is an inevitable consequence of evolving projects with a
68 | diversity of contributors. In the unlikely event that such change is not
69 | a consideration then these guidelines need not apply.
70 | * The overarching goal of the API Special Interest Group is to encourage
71 | consistency in the APIs of all the services. The proposed solutions for
72 | enabling compatibility and stability are guidance towards achieving
73 | consistency. The assertions about when a change will violate
74 | interoperability are true independent of any given solution.
75 |
76 | Any service which does not support versioning and wishes to achieve
77 | interoperability SHOULD do two things:
78 |
79 | * Add support for versioning.
80 | * Strive to follow these guidelines in a best-faith manner and where not
81 | possible consider changes with regard to reasonably expected behavior
82 | in client code.
83 |
84 | .. note:: A project which does not have a robust system for managing version
85 | boundaries but must make changes to their API is by definition not
86 | adhering to these guidelines for interoperability. The project itself
87 | must make the decisions on how to evolve their API. If this leads to
88 | conflicts with other projects within the OpenStack ecosystem, the
89 | role of the API-SIG is solely to help clarify the guidelines and
90 | provide advice on how to minimize the impact of changes. The
91 | Technical Committee is the body which provides adjudication and
92 | mediation when consensus cannot be reached.
93 |
94 | Whether a service follows the guidelines or not, any service should always
95 | strive to minimize API changes and version increases as that exacerbates the
96 | need for users to "keep up" with the changes.
97 |
98 | A service which is new and under development will obviously be undergoing a
99 | great deal of change in its early days. During this time versioning is
100 | not an indicator or tool for stability. However, once there has been a
101 | release or a dependency created with another service, stability and
102 | compatibility become critical if interoperability is desired.
103 |
104 | Definitions
105 | ===========
106 |
107 | For the sake of these guidelines the terms `compatibility` and `stability`
108 | are given fairly narrow definitions:
109 |
110 | **compatibility**
111 | An API is compatible when client code written for that service on cloud
112 | `A` will work without changes with the same service on cloud `B`.
113 |
114 | **stability**
115 | An API is stable when client code written at time `X` will continue to
116 | work without changes at future time `Y`.
117 |
118 | These definitions assume that client code is written to follow the
119 | standards of HTTP.
120 |
121 | When there is doubt in how these definitions should be applied to a situation
122 | consideration should be given first to the perspective and needs of the end
123 | users of the API (that is, those individuals who wish to use the same code
124 | against multiple clouds), second to the deployers and admins of the clouds,
125 | and third to the developers of the service.
126 |
127 | Evaluating API Changes
128 | ======================
129 |
130 | There are two types of change which **do not** require a version change:
131 |
132 | * The change is required to fix a security bug that is so severe that it
133 | requires backporting to all supported releases of the service.
134 |
135 | This case should be rare. For many less severe security situations the right
136 | answer is to treat the problem as a bug, make the fix, make a new version and
137 | release, and suggest people upgrade. The `OpenStack Vulnerability Management
138 | Team `_ has the
139 | expertise to determine the true severity of a security bug.
140 |
141 | * A bug in the API service which results in a client getting a response
142 | with a status code in the ``500-599`` range being fixed to return an
143 | informative error response in the ``400-499`` range (when the
144 | request was erroneous but fixable) or responding with success (when
145 | the request was properly formed, but the server had broken
146 | handling).
147 |
148 | The following changes **do** require a version change:
149 |
150 | * Adding a new URL.
151 |
152 | This may seem backwards-compatible, as old clients would never use
153 | the new URL, but it breaks interoperability between clouds
154 | presenting the same version of the API but using different code.
155 |
156 | * Changing the response status code from one form of client error to another
157 | (e.g., ``403`` to ``400``) or one form of success to another (e.g., ``201``
158 | to ``204``).
159 |
160 | There continues to be debate on this topic with regard to changing success
161 | codes. A robust client could effectively ride through changes in success if
162 | it treated anything from ``200`` to ``299`` as success. This requires a
163 | different standard of client than these guidelines assume. Because there is
164 | already a great deal of client code out in the OpenStack ecosystem, enforcing
165 | a client-side standard such as the `tolerant reader`_ concept, is not
166 | possible.
167 |
168 | * Adding or removing a request or response header.
169 |
170 | * Changing the value of a response header which would change how the response
171 | should be processed by the client. For example changing the value of the
172 | ``Content-Type`` header to add a new media-type.
173 |
174 | * Adding or removing a property in a resource representation in either a
175 | request or a response.
176 |
177 | * Changing the semantics or type of an existing property in a resource
178 | representation (request or response).
179 |
180 | * Changing the set of values allowed in a resource property, while
181 | maintaining its type.
182 |
183 | For example if a property once accepted "foo", "bar", or "baz" and "zoom"
184 | was added as a legitimate value, that would require a version. If "foo" was
185 | removed that too would require a version. Both addition and removal are
186 | relevant here because we want two different clouds at the same API version
187 | (but with potentially different code releases) to behave the same. To get
188 | that, even apparently backwards compatible changes require a version change.
189 |
190 | The following changes are possible if a version change is made but due
191 | consideration should be given to the impact this will have on existing users.
192 | At some point the user will want access to new functionality that is in higher
193 | versions or the minimum version of the service will be raised beyond the
194 | version where the change happens. The compensating changes in client code will
195 | be significant when any change is made, but especially so for these.
196 |
197 | * A change such that a request which was successful before now results in an
198 | error response (unless the success reported previously was hiding an
199 | existing error condition).
200 |
201 | * Removing a URL.
202 |
203 | Examples
204 | ========
205 |
206 | In many cases it will feel like a change is special and violation of these
207 | guidelines is warranted. Please consider the following scenarios:
208 |
209 | *"The change is needed to improve API consistency."*
210 |
211 | Your desire to improve API consistency is appreciated and desired, but all
212 | APIs have warts. Inconsistencies that need breaking changes could be fixed in
213 | a new API version but it isn't always necessary. Another option is to add a
214 | new URL with the different behavior. Consider all the options, finding a way
215 | to channel your efforts into improving the overall experience of using the
216 | API.
217 |
218 | *"It is unlikely that any existing users of the API would be affected."*
219 |
220 | It is difficult to predict how people are using the APIs. Developers do the
221 | strangest things. As our APIs become more adopted over time, it will only
222 | become more futile to attempt to make such predictions. An exception to this
223 | rule is when the functionality being changed is something that was literally
224 | non-functional.
225 |
226 | *"The existing API is not well documented."*
227 |
228 | If an API's behavior isn't adequately :doc:`documented `, then
229 | developers using the API have no choice but to go by what they observe the
230 | behavior to be. A change that will violate those observations is a change that
231 | requires a version.
232 |
233 | *"The change does not impact users of OpenStack's client libraries or
234 | command line interfaces."*
235 |
236 | We encourage developers to develop against OpenStack REST API. There will
237 | be many tools and applications which favor the REST API over our libraries
238 | or command line interfaces.
239 |
240 | New or Experimental Services and Versioning
241 | ===========================================
242 |
243 | As stated above, a brand new service should not commit to stability (as
244 | defined here) too early in its development. Only once some form of stability
245 | (in the standard English sense) has been reached is it worth considering.
246 |
247 | A project which has an existing stable service that wants to experiment with
248 | new functionality that it may choose to never stabilize should publish that
249 | experimental service at a unique endpoint in the service catalog, separate
250 | from the existing service.
251 |
252 | .. _tolerant reader: https://martinfowler.com/bliki/TolerantReader.html
253 |
254 | References
255 | ==========
256 |
257 | * Mailing list discussion, "Standardizing status codes in the native API
258 | (July 2012)".
259 | http://lists.openstack.org/pipermail/openstack-dev/2012-July/thread.html#132
260 | * Mailing list discussion, "refreshing and revalidating api compatibility
261 | guidelines (January 2017)".
262 | http://lists.openstack.org/pipermail/openstack-dev/2017-January/thread.html#110384
263 | * Blog posting, "Interop API Requirements (February 2017)".
264 | https://blog.leafe.com/interop-api-requirements/
265 | * The review that created this document.
266 | https://review.openstack.org/#/c/421846/
267 |
--------------------------------------------------------------------------------
/guidelines/compatibility.rst:
--------------------------------------------------------------------------------
1 | .. _compatibility:
2 |
3 | Compatibility
4 | =============
5 |
6 | This document has been superseded by :ref:`interoperability` which provides
7 | guidance on how to ensure an API is both compatible across changes and
8 | interoperable between different installations.
9 |
--------------------------------------------------------------------------------
/guidelines/consuming-catalog.rst:
--------------------------------------------------------------------------------
1 | .. _consuming-catalog:
2 |
3 | =========================
4 | Consuming Service Catalog
5 | =========================
6 |
7 | This document describes the process to correctly find a service's endpoint
8 | from the Service Catalog.
9 |
10 | .. note:: The process described in this document is compatible with all known
11 | OpenStack Public Clouds and also matches the behavior of the python
12 | library keystoneauth, which is the reference implementation of
13 | authenticating with keystone and consuming information from the
14 | catalog. In some places an argument can be made for a different
15 | process, but given keystoneauth's wide use and reference nature,
16 | we've chosen to keep backwards compatibility with keystoneauth's
17 | behavior rather than design a new perfect process. keystoneauth
18 | itself notes internally places where it kept backwards compatibility
19 | with the libraries that predate it. Notes have been left about
20 | stricter behavior a library or framework could choose to impose.
21 |
22 | .. note:: The use of the word "object" in this document refers to a JSON
23 | object, not an Object from any particular programming language.
24 |
25 | .. _catalog-user-request:
26 |
27 | User Request
28 | ============
29 |
30 | .. note:: It is worth noting that 'user' is a maleable concept. For instance,
31 | the shade library performs service discovery on behalf of its users
32 | so does not expect its users to provide a 'service-type'. In that
33 | case, shade is the 'user' of the keystoneauth library which is the
34 | discovery implementation. It is definitely not required that all
35 | consumers of OpenStack clouds know all of these things.
36 |
37 | The ultimate goal of this process is for a user to find the information about
38 | an endpoint for a service given some inputs. The user will start the process
39 | knowing some number of these parameters. Each additional input expected from
40 | the user without an answer of "where do they learn this information" will
41 | increase the difficulty of a user consuming services, so client libraries and
42 | utilities are strongly encouraged to do whatever they can to be extra helpful
43 | in helping the user ask the right question.
44 |
45 | .. note:: Be liberal with what you accept and strict with what you emit.
46 |
47 | The following is a list of such pieces of information that can be provided
48 | as user input. When an implementation exposes the ability for a user to
49 | express these parameters it is **STRONGLY** recommended that these names
50 | be used, as they show up across the OpenStack ecosystem and make discussion
51 | easier.
52 |
53 | It is assumed that the user has an ``{auth-url}`` and authentication
54 | information. The authentication process itself is out of the scope of this
55 | document.
56 |
57 | Required Inputs
58 | ---------------
59 |
60 | There is one piece of information that is absolutely required that the
61 | user know.
62 |
63 | ``service-type``
64 | The official name of the service, such as ``compute``, ``image`` or
65 | ``block-storage`` as listed in the :doc:`OpenStack Service Types Authority
66 | `.
67 | Required. It is impossible for a user to consume service discovery without
68 | knowing what service they want to discover.
69 |
70 | Optional Filters
71 | ----------------
72 |
73 | There are several optional pieces of information that the user might know,
74 | or additional constraints the user might wish to express to control how the
75 | endpoints for a service are selected.
76 |
77 | ``region-name``
78 | The region of the service the user desires to work with. May be optional,
79 | depending on whether the cloud has more than one region. Services
80 | all exist within regions, but some clouds only have one region.
81 | If ``{be-strict}`` (see below) has been given, ``{region-name}`` is required.
82 |
83 | .. note:: It is highly recommended that ``{region-name}`` always be required
84 | to protect against single-region clouds adding a region in the
85 | future. However, the canonical OpenStack implementation
86 | *keystoneauth* today allows region name to be omitted and there are
87 | a large number of clouds in existence with a single region named
88 | ``RegionOne``. For completely new libraries or major versions
89 | where breaking behavior is acceptable, requiring region name
90 | by default would be preferred, but breaking users just to introduce
91 | the restriction is discouraged.
92 |
93 | ``interface``
94 | Which API interface, such as ``public``, ``internal`` or ``admin``, that
95 | the user wants to use. A user should be able to request a list of interfaces
96 | they find acceptable in the order of their preference, such as
97 | ``['internal', 'public']`` (Optional, defaults to ``public``)
98 |
99 | ``endpoint-version`` OR ``min-endpoint-version``, ``max-endpoint-version``
100 | The **major** version of the service the user desires to work with. Optional.
101 |
102 | An endpoint version is inherently a range with a minimum and a maximum value.
103 | Whether it is presented to the user as a single parameter or a pair of
104 | parameters is an implementation detail.
105 |
106 | Each endpoint version is a string with one (``3``) or two (``3.1``) numbers,
107 | separated by a dot.
108 |
109 | .. warning:: Care has to be taken to not confuse major versions consisting
110 | of two numbers with microversions. Microversions usually exist
111 | within a certain major version, and also have a form of ``X.Y``.
112 | No services currently use both major versions and microversions
113 | in the form of ``X.Y``.
114 |
115 | .. TODO(dtantsur): so, what if a service has both major versions in the form
116 | of ``X.Y`` and microversions?
117 |
118 | Version strings are not decimals, the are a tuple of 2 numbers combined with
119 | a dot. Therefore, ``3.10`` is higher than ``3.9``.
120 |
121 | A user can omit the endpoint-version indicating that they want to use
122 | whatever endpoint is in the ``{service-catalog}``.
123 |
124 | A user can desire to work with the latest available version, in which
125 | case the ``{endpoint-version}`` should be ``latest``. If s
126 | ``{min-endpoint-version}`` is ``latest``, ``{max-endpoint-version}`` must be
127 | omitted or also ``latest``.
128 |
129 | A version can be specified with a minor value of ``latest`` to indicate
130 | the highest minor version of a given major version. For instance,
131 | ``3.latest`` would match the highest of ``3.3`` and ``3.4`` but not ``4.0``.
132 |
133 | If the parameter is presented as a single string, a single value should be
134 | interpreted as if ``{min-endpoint-version}`` is the value given and
135 | ``{max-endpoint-version}`` is ``MAJOR.latest``. For instance, if ``3.4`` is
136 | given as a single value, ``{min-endpoint-version}`` is ``3.4`` and
137 | ``{max-endpoint-version}`` is ``3.latest``.
138 |
139 | It may seem strange from an individual user perspective to want a range or
140 | ``latest`` - but from a library and framework perspective, things like shade
141 | or terraform may have internal logic that can handle more than one version of
142 | a service and want to use the best version available.
143 |
144 | .. note:: Guidance around 'latest' is different from that found in
145 | :ref:`the microversion specification
146 | `. It is acceptable for a client
147 | library or framework to be interested in the latest version
148 | available but such a specification is internal and not sent to
149 | the server. In the client case with major versions, ``latest`` acts
150 | as an input to the version discovery process.
151 |
152 | ``service-name``
153 | Arbitrary name given to the service by the deployer. Optional.
154 |
155 | .. note:: In all except the most extreme cases this should never be needed
156 | and its use as a meaningful identifier by Deployers is strongly
157 | discouraged. However, the Consumer has no way to otherwise mitigate
158 | the situation if their Deployer has provided them with a catalog
159 | where a ``service-name`` must be used, so ``service-name`` must be
160 | accepted as input.
161 | If ``{be-strict}`` (see below) has been requested, a user supplying
162 | ``{service-name}`` should be an error.
163 |
164 | ``service-id``
165 | Unique identifier for an endpoint in the catalog. Optional.
166 |
167 | .. note:: On clouds with well-formed catalogs ``service-id`` should never be
168 | needed. If ``{be-strict}`` has been requested, supplying
169 | ``{service-id}`` should be an error.
170 |
171 | ``endpoint-override``
172 | An endpoint for the service that the user has procured from some other
173 | source. (Optional, defaults to omitted.)
174 |
175 | Discovery Behavior Modifiers
176 | ----------------------------
177 |
178 | The user may also wish to express alterations to the general algorithm.
179 | Implementations may present these flags under any name that makes sense,
180 | or may choose to not present them as behavioral modification options at all.
181 |
182 | ``be-strict``
183 | Forgo leniant backwards compatibility concessions and be more strict in
184 | input and output validation. Defaults to False.
185 |
186 | ``skip-discovery``
187 | If the user wants to completely skip the version discovery process even if
188 | logic would otherwise do it. This is useful if the user has specified an
189 | ``{endpoint-override}`` or they know they just want to use whatever is in
190 | the catalog and do not need additional metadata about the endpoint. Defaults
191 | to False
192 |
193 | ``fetch-version-information``
194 | If the user has specified an ``{endpoint-version}`` which can be known to
195 | match just from looking at the URL, the version discovery process will not
196 | fetch version information documents. However, the user may need the
197 | information, such as microversion ranges. Using
198 | ``{fetch-version-information}`` allows them to request that the version
199 | document be fetched even when an optimization in the process would otherwise
200 | allow fetching the document to be skipped. Defaults to False.
201 |
202 |
203 | Discovery Results
204 | =================
205 |
206 | At the end of the discovery process, the user should know the following:
207 |
208 | If the process was successful:
209 |
210 | * The actual values found for all of the input values above.
211 |
212 | Found values will be referred to in these documents as ``found-{value}`` to
213 | differentiate. So if a user requested an ``{endpoint-version}`` of
214 | ``latest``, ``{found-endpoint-version}`` might be ``3.5``.
215 |
216 | * ``service-endpoint``
217 | The endpoint to use as the root of the service.
218 |
219 | * ``max-version``
220 | If the service supports microversions, what is the maximum microversion the
221 | service supports. Optional, defaults to omitted, which implies that
222 | microversions are not supported.
223 |
224 | * ``min-version``
225 | If the service supports microversions, what is the minimum microversion the
226 | service supports. Optional, defaults to omitted, which implies that
227 | microversions are not supported.
228 |
229 | If the process was unsuccessful, an error should be returned explaining which
230 | part failed. For instance, was a matching service not found at all or was
231 | a matching version not found. If a matching version was not found, the error
232 | should contain a list of version that were found.
233 |
234 | In the description that follows, each of the above inputs and outputs will
235 | be referred to like ``{endpoint-override}`` so that it is clear whether a user
236 | supplied input to the process or one of the expected outputs is being
237 | discussed. Other values that are fetched at one point in the process and
238 | referred to at a later point are similarly referred to like
239 | ``{service-catalog}``. Names will not be reused within the process to
240 | hold different content at different times.
241 |
242 | Discovery Algorithm
243 | ===================
244 |
245 | Services should be registered in the ``{service-catalog}`` using their
246 | ``{service-type}`` from the :doc:`OpenStack Service Types Authority
247 | `. However, for historical reasons there are some
248 | services that have old service types found in the wild. To facilitate moving
249 | forward with the correct ``{service-type}`` names, but also support existing
250 | users and installations, the OpenStack Service Types Authority contains a list
251 | of historical aliases for such services.
252 |
253 | Clients will need a copy of the data published in the
254 | OpenStack Service Types Authority to be able to complete the full Discovery
255 | Algorithm. A client library could either keep a local copy or fetch the data
256 | from https://service-types.openstack.org/service-types.json and potentially
257 | cache it. It is recommended that client libraries handle consumption of the
258 | historical data for their users but also allow some mechanism for the user to
259 | provide a more up to date verison of the data if necessary.
260 |
261 | The basic process is:
262 |
263 | #. Authenticate to keystone at the ``{auth-url}``, retreiving a ``token``
264 | which contains the ``{service-catalog}``.
265 |
266 | .. note:: This step is obviously skipped for clouds without authentication.
267 |
268 | #. If the user has provided ``{endpoint-override}``, it is used as
269 | ``{catalog-endpoint}``.
270 |
271 | #. If the user has not provided ``{endpoint-override}``, retrieve matching
272 | ``{catalog-endpoint}`` from the ``{service-catalog}`` using the procedure
273 | explained in :doc:`consuming-catalog/endpoint`.
274 |
275 | #. If ``{skip-discovery}`` is true, STOP and use ``{catalog-endpoint}`` as
276 | ``{service-endpoint}``. Otherwise, discover the available API versions
277 | and find the suitable ``{service-endpoint}`` using the version discovery
278 | procedure from :doc:`consuming-catalog/version-discovery`.
279 |
280 | #. If the requested ``{service-type}`` is an alias of an official type in the
281 | OpenStack Service Types Authority and any endpoints match the official
282 | type, :ref:`find-endpoint-matching-best-service-type`.
283 |
284 |
285 | Table of Contents
286 | =================
287 |
288 | .. toctree::
289 |
290 | consuming-catalog/endpoint
291 | consuming-catalog/version-discovery
292 | consuming-catalog/authority
293 |
--------------------------------------------------------------------------------
/guidelines/consuming-catalog/authority.rst:
--------------------------------------------------------------------------------
1 | Consuming Service Types Authority
2 | =================================
3 |
4 | The `OpenStack Service Types Authority`_ is data about official service type
5 | names and historical service type names commonly in use from before there was
6 | an official list. It is made available to allow libraries and other client
7 | API consumers to be able to provide a consistent interface based on the
8 | official list but still support existing names. Providing this support is
9 | highly recommended, but is ultimately optional. The first step in the matching
10 | process is always to return direct matches between the catalog and the user
11 | request, so the existing consumption models from before the existence of the
12 | authority should always work.
13 |
14 | In order to consume the information in the `OpenStack Service Types Authority`_
15 | it is important to know a few things:
16 |
17 | #. The data is maintained in YAML format in git. This is the ultimately
18 | authoritative source code for the list.
19 |
20 | #. The data is published in JSON format at
21 | https://service-types.openstack.org/service-types.json and has a JSONSchema
22 | at https://service-types.openstack.org/published-schema.json.
23 |
24 | #. The published data contains a version which is date based in
25 | `ISO Date Time Format`_, a sha which contains the git sha of the
26 | commit the published data was built from, and pre-built forward and reverse
27 | mappings between official types and aliases.
28 |
29 | #. The JSON file is served with ETag support and should be considered highly
30 | cacheable.
31 |
32 | #. The current version of the JSON file should always be the preferred file to
33 | use.
34 |
35 | #. The JSON file is similar to timezone data. It should not be considered
36 | versioned such that stable releases of distros should provide a
37 | frozen version of it. Distro packages should instead update for all
38 | active releases when a new version of the file is published.
39 |
40 |
41 | .. _OpenStack Service Types Authority: https://opendev.org/openstack/service-types-authority/
42 | .. _ISO Date Time Format: https://tools.ietf.org/html/rfc3339#section-5.6
43 |
--------------------------------------------------------------------------------
/guidelines/counting.rst:
--------------------------------------------------------------------------------
1 | Counting Resources
2 | ==================
3 |
4 | This topic document serves to provide guidance on returning the total size of
5 | a resource collection in a project's public REST API; this is useful when the
6 | total number of resources may be larger than the number of resources being
7 | enumerated in a single response.
8 |
9 | Guidance
10 | --------
11 |
12 | The 'with_count' query string parameter is used to indicate if the total count
13 | of resources should or should not be returned from a GET REST API request. Any
14 | value that equates to True indicates that the count should be returned;
15 | conversely, any value that equates to False indicates that the count should
16 | not be returned.
17 |
18 | If the 'with_count' query string parameter is absent, the server may still
19 | include the count; however, it should only do so if determining the count is
20 | trivial and does not require, for example, an additional database query. When
21 | the count is potentially expensive to obtain, it should only be included if it
22 | is explicitly requested.
23 |
24 | In the JSON reply, the count value is an integer and is associated with the
25 | top-level property 'count'. For example, when retrieving servers, if
26 | 'with_count=true' is supplied then the total count should be included in the
27 | reply as::
28 |
29 | {
30 | "servers": [...],
31 | "count":
32 | }
33 |
--------------------------------------------------------------------------------
/guidelines/discoverability.rst:
--------------------------------------------------------------------------------
1 | .. _discoverability:
2 |
3 | API Discoverability
4 | ===================
5 |
6 | This topic document serves to provide guidance on how to have a public REST
7 | API expose the URIs and resources to end users in a machine-readable way.
8 |
9 | See also the topic document on :ref:`consuming-catalog`.
10 |
11 | See also the topic document on :doc:`consuming-catalog/version-discovery`.
12 |
13 | .. _versioned-and-unversioned-endpoints:
14 |
15 | Versioned and Unversioned Endpoints
16 | -----------------------------------
17 |
18 | Each service should have a base endpoint which is referred to as the
19 | "Unversioned" endpoint for the service.
20 |
21 | .. note:: It is highly recommended that Cloud Operators register the
22 | Unversioned Endpoint for a service in the Keystone Catalog. If they
23 | do, the process described in the :ref:`version-discovery-algorithm` will
24 | be able to be both the most featureful for the API Consumer and the
25 | most efficient.
26 |
27 | Each service must have at least one Major API version.
28 |
29 | If a service has, or expects to have, more than one Major API version,
30 | each of those versions should have a base endpoint which is referred to
31 | as the "Versioned" endpoint for that version of the service.
32 |
33 | All version discovery documents must be accessible via unauthenticated
34 | connection.
35 |
36 | If a service only uses microversions and only has one Major API version,
37 | that service should not have any additional "Versioned" endpoints.
38 |
39 | For instance, the Glance service at cloud ``example.com`` might have an
40 | Unversioned Endpoint at::
41 |
42 | https://image.example.com
43 |
44 | That Glance service may then also provide three major versions, `v1`, `v2`, and
45 | `v3`::
46 |
47 | https://image.example.com/v1
48 | https://image.example.com/v2
49 | https://image.example.com/v3
50 |
51 | Additionally, the Placement service at cloud ``example.com`` might have
52 | an Unversioned Endpoint at::
53 |
54 | https://placement.example.com
55 |
56 | The Placement service only uses microversions, so there are no additional
57 | Versioned Endpoints.
58 |
59 | In both cases, the Unversioned Endpoint is the endpoint recommended to be
60 | registered in the Service Catalog.
61 |
62 | Preference for Subpaths
63 | ~~~~~~~~~~~~~~~~~~~~~~~
64 |
65 | Historically each of the OpenStack services were also given a port. It is
66 | strongly recommended to not use those ports and instead use the normal port
67 | 443 for https. If multiple API services are to be installed on a single
68 | machine, it is highly recommended to use subpaths::
69 |
70 | https://api.example.com/compute
71 | https://api.example.com/image
72 |
73 | The rationale behind this recommendation is to ease use for users who may have
74 | restrictive port-blocking firewalls in their operating environment. Since the
75 | traffic is HTTP traffic and not a different protocol, it is not necessary to
76 | distinguish it by port number, and doing so increases the chances that users
77 | will have problems connecting to individual API endpoints.
78 |
79 | .. _version-discovery:
80 |
81 | Version Discovery
82 | -----------------
83 |
84 | Each service should provide a Version Discovery API on both the Unversioned
85 | Endpoint and each of Versioned Endpoints of the service to be used by clients
86 | to discover the supported API versions.
87 |
88 | .. _unversioned-version-discovery:
89 |
90 | Unversioned Discovery
91 | ~~~~~~~~~~~~~~~~~~~~~
92 |
93 | Each service should provide a Version Discovery API at the Unversioned Endpoint
94 | of the service. It should be exposed to all users without authentication.
95 |
96 | .. note:: It is recommended that the Version Discovery API not be protected
97 | by authentication requirements. The information returned is not
98 | specific to a user, but is, instead, a fundamental characteristic
99 | of the base API of the service running. Additionally, the v2 and
100 | v3 authentication API are different, so requiring authentication
101 | before version discovery makes it harder to determine reliably
102 | whether v2 or v3 authentication should be used.
103 |
104 | The Unversioned Version Discovery API for each service should return a list of
105 | Version Information for all of the Base Endpoints the service provides,
106 | along with that version's minimum and maximum microversions. These values are
107 | used by the client to discover the supported API versions.
108 |
109 |
110 | :download:`Version Information Schema `
111 |
112 | .. literalinclude:: version-information-schema.json
113 | :language: json
114 |
115 | :download:`Version Discovery Schema `
116 |
117 | .. literalinclude:: version-discovery-schema.json
118 | :language: json
119 |
120 | .. _unversioned-discovery-response:
121 |
122 | An Unversioned Version Discovery response would look as follows:
123 |
124 | .. code-block::
125 |
126 | GET /
127 |
128 | .. code-block:: json
129 |
130 | {
131 | "versions": [
132 | {
133 | "id": "v2.1",
134 | "links": [
135 | {
136 | "href": "https://compute.example.com/v2/",
137 | "rel": "self"
138 | },
139 | {
140 | "href": "https://compute.example.com/",
141 | "rel": "collection"
142 | }
143 | ],
144 | "status": "CURRENT",
145 | "max_version": "5.2",
146 | "min_version": "2.1"
147 | },
148 | ]
149 | }
150 |
151 | Each Version Information in the list should contain the following information:
152 |
153 | id
154 | The major API version. Follows format outlined in :ref:`versioning`,
155 | preceeded by a "v". Required.
156 |
157 | links
158 | Contains information about where to find the actual versioned endpoint. See
159 | :ref:`version-links` below. Required.
160 |
161 | status
162 | Support and lifecycle status of the versioned endpoint. Required.
163 | See :ref:`endpoint-status`
164 |
165 | max_version
166 | The maximum microversion available if the version of the service supports
167 | microversions. Optional. See :ref:`microversion_specification`
168 |
169 | min_version
170 | The minimum microversion available if the version of the service supports
171 | microversions. Optional. See :ref:`microversion_specification`
172 |
173 | If a service has no Versioned Endpoints, it should simply list its Base
174 | Endpoint in the document, like so:
175 |
176 | .. code-block::
177 |
178 | GET /
179 |
180 | .. code-block:: json
181 |
182 | {
183 | "versions": [
184 | {
185 | "id": "v1.0",
186 | "links": [
187 | {
188 | "href": "https://placement.example.com/",
189 | "rel": "self"
190 | },
191 | {
192 | "href": "https://placement.example.com/",
193 | "rel": "collection"
194 | }
195 | ],
196 | "status": "CURRENT",
197 | "max_version": "1.25",
198 | "min_version": "1.0"
199 | }
200 | ]
201 | }
202 |
203 | .. _versioned-version-discovery:
204 |
205 | Versioned Discovery
206 | ~~~~~~~~~~~~~~~~~~~
207 |
208 | Each service should provide a Version Discovery API at each Versioned Endpoint
209 | of the service. It should be exposed to all users without authentication.
210 |
211 | The document returned from the Version Discovery API for Versioned Endpoint
212 | should be identical to the document returned from the Unversioned Endpoint.
213 | In this way, a client that is looking for a version of an API can always
214 | get the complete information in one step, rather than a sequence of attempts,
215 | failures and re-attempts.
216 |
217 | However, in service of getting to a perfect future from amidst an imperfect
218 | past, services that already deliver a different document on their Versioned
219 | Endpoints who are concerned with API breakage resulting from changing the
220 | payload of their Versioned Version Discovery Document from a single object
221 | named ``version`` to a list of objects named ``versions``, it can be
222 | nonetheless a step forward to add a link to the list of links provided
223 | that points to the Unversioned Discovery Endpoint.
224 |
225 | For services that do not return the Versioned Version Discovery Document
226 | inside of an object named ``version`` but instead with the information
227 | directly in the root object, it is similarly suggested to add the
228 | ``collection`` link. (see
229 | https://www.iana.org/assignments/link-relations/link-relations.xhtml for
230 | the list of defined relation types)
231 |
232 | :download:`Versioned Discovery Schema `
233 |
234 | .. literalinclude:: versioned-discovery-schema.json
235 | :language: json
236 |
237 | For example:
238 |
239 | .. code-block::
240 |
241 | GET /v2
242 |
243 | .. code-block:: json
244 |
245 | {
246 | "version": {
247 | "id": "v2.0",
248 | "links": [
249 | {
250 | "href": "https://image.example.com/v2",
251 | "rel": "self"
252 | },
253 | {
254 | "href": "https://image.example.com/",
255 | "rel": "collection"
256 | }
257 | ],
258 | "status": "CURRENT"
259 | }
260 | }
261 |
262 | .. _endpoint-status:
263 |
264 | Endpoint Status
265 | ---------------
266 |
267 | While it is typical for there to only be a single versioned API for a given
268 | service, it is also sometimes useful to be able to offer more than one version
269 | of a given API. Common examples of this are when an older version is still
270 | made available in order to support clients that have not yet upgraded to the
271 | current version, or when a new API version is being tested before it is
272 | released. To distinguish these different APIs for the same service, the
273 | `status` value is used. The following values can be returned for status:
274 |
275 | CURRENT
276 | The newest API that is currently being developed and improved.
277 | Unless you need to support old code, use this API. One and only
278 | one API must be marked as CURRENT.
279 |
280 | SUPPORTED
281 | An older version of the API. No new features will be added to this version,
282 | but any bugs discovered in the code may be fixed.
283 |
284 | DEPRECATED
285 | This API will be removed in the foreseeable future. You should start
286 | planning on using alternatives.
287 |
288 | EXPERIMENTAL
289 | This API is under development ('alpha'), and you can expect it to change or
290 | even be removed.
291 |
292 | .. _version-links:
293 |
294 | Version Links
295 | -------------
296 |
297 | .. note:: The ``links`` conform to the :ref:`links` guideline.
298 |
299 | The ``links`` field of the endpoint description should contain at least
300 | two entries, listed here by the value of their ``rel`` field.
301 |
302 | self
303 | The location of the base endpoint for the given version of the service.
304 |
305 | collection
306 | The location of the base endpoint for the Unversioned Version Discovery
307 | Endpoint. The ``collection`` entry provides guidance to allow a client to
308 | navigate from a Versioned Endpoint that may have been listed in the
309 | Service Catalog to the Unversioned Endpoint if they are looking for a
310 | different version without resorting to attempting to guess at URL schemes
311 | and performing URL manipulation.
312 |
313 | Guidance
314 | --------
315 |
316 | **TODO** Add sections that describe a best practice for API discoverability,
317 | possibly using JSON-Home, JSONSchema documents, and JSON-HAL.
318 |
--------------------------------------------------------------------------------
/guidelines/dns-sd.rst:
--------------------------------------------------------------------------------
1 | DNS-based Service Discovery
2 | ===========================
3 |
4 | The normal way to discover OpenStack services is via the :doc:`service catalog
5 | `. However, in some edge cases it may be convenient to use
6 | DNS-based discovery, for example:
7 |
8 | #. Initial discovery of the Identity service endpoint via DNS.
9 | #. Discovery of a service on the local network, especially in standalone case.
10 |
11 | .. warning::
12 | This guideline does not endorse any services to implement any DNS-based
13 | discovery, but rather serves as guidance for services and deployments
14 | that need it.
15 |
16 | This guideline is heavily based on two IETF documents:
17 |
18 | * `RFC 6763`_ defines DNS service discovery.
19 | * `RFC 6762`_ defines the Multicast DNS protocol (or mDNS).
20 |
21 | Service Type
22 | ------------
23 |
24 | `RFC 6763`_ section 7 defines a fully qualified domain name for a service in
25 | the following format::
26 |
27 | .._tcp..
28 |
29 | where:
30 |
31 | ``domain``
32 | is a parent domain that the service belong to. It must be ``local`` for
33 | multicast DNS.
34 | ``servicename``
35 | is a service-specific protocol name, which in case of OpenStack API
36 | discovery MUST be ``_openstack``.
37 | ``instance``
38 | is an instance of a service, which in case of OpenStack API discovery MUST
39 | be an OpenStack service type, such as ``compute``, ``identity`` or
40 | ``baremetal``. Project code names, such as ``nova``, MUST NOT be used.
41 |
42 | Service Information
43 | -------------------
44 |
45 | The DNS ``SRV`` record will provide a host and port number for the service.
46 | DNS ``TXT`` records SHOULD be used to communicate the remaining parts required
47 | to access a service. `RFC 6763`_ defines the format of these records to be
48 | key-value pairs separated by ``=``. This guideline defines the following keys:
49 |
50 | ``path``
51 | SHOULD be used to specify the path part of the endpoint. If missing, ``/``
52 | MUST be assumed.
53 |
54 | .. note::
55 | It's tempting to prefix the keys defined here with something like
56 | ``os_``, but ``path`` is used in the examples in `RFC 6763`_ in exactly
57 | the same role, so it may be familiar enough to potential consumers
58 | and tools.
59 | ``protocol``
60 | SHOULD be used to specify whether to use HTTP or HTTPS. If present, it MUST
61 | be ``http`` or ``https``. If absent, a consumer SHOULD use the port to
62 | decide which protocol to use:
63 |
64 | * if port is 80, use HTTP,
65 | * if port is 443, use HTTPS,
66 |
67 | If port is not one of 443 and 80, a consumer SHOULD try HTTPS and MAY fall
68 | back to HTTP if it fails.
69 | ``txtvers``
70 | SHOULD be used as defined in `RFC 6763`_ to designate the version of the
71 | format. If present, its value MUST be ``1``.
72 |
73 | Examples
74 | --------
75 |
76 | Identity discovery for a provider
77 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78 |
79 | As a new user of OpenStack provider ``mystack.example.com``, I would like to
80 | discover the ``auth_url`` to use.
81 |
82 | I issue a DNS request to retrieve ``SRV`` and ``TXT`` records::
83 |
84 | $ nslookup -query=any "identity._openstack._tcp.mystack.example.com"
85 | identity._openstack._tcp.mystack.example.com service = 0 0 443 os.mystack.example.com
86 | identity._openstack._tcp.mystack.example.com text = "txtvers=1" "path=/"
87 |
88 | Now I know that I have to connect to ``os.mystack.example.com``, port 443.
89 | From the ``TXT`` records I know that I should use the root path. The protocol
90 | is not specified, so from port 443 I derive using HTTPS.
91 |
92 | Result: ``auth_url`` is ``https://os.mystack.example.com/``.
93 |
94 | Local discovery of ironic
95 | ~~~~~~~~~~~~~~~~~~~~~~~~~
96 |
97 | The ironic service ramdisk needs to discover baremetal (ironic) and baremetal
98 | introspection (ironic-inspector) API endpoints after start up. The service
99 | catalog is not available to it.
100 |
101 | The ramdisk issues a multicast DNS request to list OpenStack services. An
102 | equivalent Avahi_ (FOSS mDNS and DNS-SD implementation) command would be::
103 |
104 | $ avahi-browse -rt _openstack._tcp
105 | + eth1 IPv4 baremetal _openstack._tcp local
106 | + eth1 IPv4 baremetal-introspection _openstack._tcp local
107 | = eth1 IPv4 baremetal _openstack._tcp local
108 | hostname = [baremetal._openstack._tcp.local]
109 | address = [192.168.42.17]
110 | port = [80]
111 | txt = ["proto=http" "path=/baremetal"]
112 | = eth1 IPv4 baremetal-introspection _openstack._tcp local
113 | hostname = [baremetal-introspection._openstack._tcp.local]
114 | address = [192.168.42.17]
115 | port = [5050]
116 | txt = ["proto=http"]
117 |
118 | Here we do a multicast search for all services matching service type
119 | ``_openstack._tcp`` defined in `Service Type`_. We receive two results for
120 | the expected services, each containing an IP address, a TCP port and ``proto``
121 | and ``path`` variables as part of its ``TXT`` section.
122 |
123 | Result:
124 |
125 | * The baremetal endpoint is ``http://192.168.42.17/baremetal``.
126 | * The baremetal introspection endpoint is ``http://192.168.42.17:5050``.
127 |
128 | .. _RFC 6763: https://www.ietf.org/rfc/rfc6763.txt
129 | .. _RFC 6762: https://www.ietf.org/rfc/rfc6762.txt
130 | .. _Avahi: https://avahi.org/
131 |
--------------------------------------------------------------------------------
/guidelines/errors-example.json:
--------------------------------------------------------------------------------
1 | {
2 | "errors": [
3 | {
4 | "request_id": "1dc92f06-8ede-4fb4-8921-b507601fb59d",
5 | "code": "orchestration.create_failed",
6 | "status": 418,
7 | "title": "The Stack could not be created",
8 | "detail": "The Stack could not be created because of error(s) in other parts of the system.",
9 | "links": [
10 | {
11 | "rel": "help",
12 | "href": "https://developer.openstack.org/api-ref/orchestration/errors/orchestration.create-failed"
13 | }
14 | ]
15 | },
16 | {
17 | "request_id": "d413ea12-dfcd-4009-8fad-229b475709f2",
18 | "code": "compute.scheduler.no-valid-host-found",
19 | "status": 403,
20 | "title": "No valid host found",
21 | "detail": "No valid host found for flavor m1.xlarge.",
22 | "links": [
23 | {
24 | "rel": "help",
25 | "href": "https://developer.openstack.org/api-ref/compute/errors/compute.scheduler.no-valid-host-found"
26 | }
27 | ]
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/guidelines/errors-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "id": "http://specs.openstack.org/openstack/api-wg/errors-schema.json#",
4 | "type": "object",
5 | "properties": {
6 | "errors": {
7 | "type": "array",
8 | "description": "An ordered list of errors with the most recent error first.",
9 | "minItems": 1,
10 | "items": {
11 | "type": "object",
12 | "description": "Additional information about problems encountered while performing an operation.",
13 | "properties": {
14 | "request_id": {
15 | "type": "string",
16 | "description": "A unique identifier for this particular occurrence of the problem. If this property is present, it MUST match the X-Openstack-Request-Id header of the response in which it occurred."
17 | },
18 | "code": {
19 | "type": "string",
20 | "pattern": "^[a-z0-9._-]+$",
21 | "description": "A service-specific error code. The general form of the code is service-type.error-code. service-type MUST be the service type as defined by the service types authority at http://specs.openstack.org/openstack/service-types-authority. error-code is defined by service project team and MUST only consist of lowercase alpha, numeric, '.', '_', and '-' characters."
22 | },
23 | "status": {
24 | "type": "integer",
25 | "description": "The HTTP status code applicable to this problem. It MUST match the status code of the response in which it occurred."
26 | },
27 | "title": {
28 | "type": "string",
29 | "description": "A short, human-readable summary of the problem. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization."
30 | },
31 | "detail": {
32 | "type": "string",
33 | "description": "A human-readable explanation specific to this occurrence of the problem."
34 | },
35 | "links": {
36 | "type": "array",
37 | "description": "An array that MUST contain at least one Link Description Object with a 'rel': 'help' and an 'href' that links to a resource that can help the user as defined by http://specs.openstack.org/openstack/api-wg/guidelines/errors.html#errors-documentation",
38 | "minItems": 1,
39 | "items": { "$ref": "http://json-schema.org/draft-04/links" }
40 | }
41 | },
42 | "required": [
43 | "code",
44 | "status",
45 | "title",
46 | "detail",
47 | "links"
48 | ]
49 | }
50 | }
51 | },
52 | "required": [
53 | "errors"
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/guidelines/errors.rst:
--------------------------------------------------------------------------------
1 | .. _errors:
2 |
3 | Errors
4 | ======
5 |
6 | Description
7 | -----------
8 |
9 | Errors are a crucial part of the developer experience when using an API. As
10 | developers learn the API they inevitably run into errors. The quality and
11 | consistency of the error messages returned to them will play a large part
12 | in how quickly they can learn the API, how they can be more effective with
13 | the API, and how much they enjoy using the API.
14 |
15 | This document describes an emerging standard within OpenStack for providing
16 | structured error responses that can be consistently processed and include
17 | coding that will eventually allow errors to be searched for on the web and in
18 | documentation using the value of the ``code`` field. Such codes help to
19 | distinguish the causes of different errors in response to the same HTTP method
20 | and URI, even when the same HTTP status is returned.
21 |
22 | .. note:: Services choosing to add these structured error responses are advised
23 | that doing so is not considered a backwards incompatible change and are
24 | encouraged to add them without needing to version the service. However,
25 | when a ``code`` is added to a specific response, subsequent change to that
26 | code, on that response, is an incompatbile change.
27 |
28 | Errors JSON Schema
29 | ------------------
30 |
31 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
32 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
33 | document are to be interpreted as described in :rfc:`2119`.
34 |
35 | .. literalinclude:: errors-schema.json
36 | :language: json
37 |
38 | Errors JSON Example
39 | -------------------
40 |
41 | .. literalinclude:: errors-example.json
42 | :language: json
43 |
44 | .. note:: This example is completely contrived. This is not how Orchestration
45 | or Compute responds with errors. It merely illustrates how a service might
46 | chain together errors. The example hrefs in the ``links`` are examples
47 | only, but demonstrate that a service should be responsible for publishing
48 | and maintaining the documentation associated with the error codes the
49 | service produces.
50 |
51 | Errors Documentation
52 | --------------------
53 |
54 | The intention of the ``code`` is twofold:
55 |
56 | * To provide a convenient and memorable phrase that a human can use when
57 | communicating with other humans about an error they've experienced, or use
58 | when searching documentation or their favorite search engine for references
59 | to the error.
60 |
61 | * To act as flow control in client code where the same HTTP status code may be
62 | used to indicate multiple conditions. A common case is when a
63 | ``409 Conflict`` may indicate several different ways in which the desired
64 | state cannot be accommodated and the handling of the conflict should differ.
65 |
66 | To satisfy both of these requirements the strings used for the error codes need
67 | to be self-describing and human-readable while also distinct from one another.
68 | Avoiding abbreviation is recommended.
69 |
70 | As an example, consider a service that provides a URI, ``/servers/{uuid}``.
71 | There are at least two different ways that a ``404 Not Found`` response may
72 | happen when making a ``GET`` request against that URI. One is that no server
73 | having ``{uuid}`` currently exists. The other is that the URI has been entered
74 | incorrectly (e.g., ``/server/{uuid}``). These conditions should result in
75 | different codes. Two possible codes for these cases are
76 | ``compute.server.not_found`` and ``compute.uri.not_found``, respectively.
77 |
--------------------------------------------------------------------------------
/guidelines/etags.rst:
--------------------------------------------------------------------------------
1 |
2 | ETags
3 | =====
4 |
5 | ETags_ are "opaque validator[s] for differentiating between
6 | multiple representations of the same resource". They are used in a
7 | variety of ways in HTTP to determine the outcome of conditional
8 | requests as described in :rfc:`7232`. Understanding the full breadth
9 | of ETags requires a very complete understanding of HTTP and the
10 | nuances of resources and their representations. This document does
11 | not attempt to address all applications of ETags at once, instead it
12 | addresses specific use cases that have arisen in response to other
13 | guidelines. It will evolve over time.
14 |
15 | ETags and the lost update problem
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 |
18 | The Problem
19 | -----------
20 |
21 | HTTP is fundamentally a system for sending representations of
22 | resources back and forth across a network connection. A common
23 | interaction is to ``GET /some/resource``, modify the representation,
24 | and then ``PUT /some/resource`` to update the resource on the server.
25 | This is an extremely useful and superfically simple pattern that drives
26 | many APIs in OpenStack and beyond.
27 |
28 | That apparently simplicity is misleading: If there are two or more
29 | clients performing operations on ``/some/resource`` in the same time
30 | frame they can experience the `lost update problem`_:
31 |
32 | * Client A and client B both ``GET /some/resource`` and make changes to
33 | their local representation.
34 | * Client B does a ``PUT /some/resource`` at time 1.
35 | * Client A does a ``PUT /some/resource`` at time 2.
36 |
37 | Client B's changes have been lost. Neither client is made aware of this.
38 | This is a problem.
39 |
40 | A Solution
41 | ----------
42 |
43 | HTTP/1.1 and beyond has a solution for this problem called ETags_.
44 | These provide a validator for different representations of a
45 | resource that make it straightforward to determine if the
46 | representation provided by a request or a response is the same as
47 | one already in hand. This is very useful when validating cached GET
48 | requests (the ETag answers the question "is what I have in my cache
49 | the same as what the server would give me?") but is also useful for
50 | avoiding the lost update problem.
51 |
52 | If the scenario described above is modified to use ETags it would
53 | work like this:
54 |
55 | * Client A and client B both ``GET /some/resource``, including a
56 | response header named ``ETag`` that is the same for both clients
57 | (let's make the ETag 'red57'). Details on ETag generation can be
58 | found below.
59 | * They both make changes to their local representation.
60 | * Client B does a ``PUT /some/resource`` and includes a header
61 | named If-Match_ with a value of ``red57``. The request is
62 | successful because the ETag sent in the request is the same as the
63 | ETag generated by the server of its current state of the resource.
64 | * Client A does a ``PUT /some/resource`` and includes the If-Match_
65 | header with value ``red57``. This request fails (with a 412_
66 | response code) because ``red57`` no longer matches the ETag
67 | generated by the server: Its current state has been updated by the
68 | request from client B.
69 |
70 |
71 | Client B's changes have not been lost and client A has not
72 | inadvertently changed something that is not in the form they
73 | expected. Client A is made aware of this by the response code.
74 | At this stage, client A can choose to GET the resource again and compare
75 | their local representation with that just retrieved and choose a course
76 | of action.
77 |
78 | Details
79 | -------
80 |
81 | If a service accepts PUT requests and needs to avoid lost updates it
82 | can do so by:
83 |
84 | * Sending responses to GET requests with an ETag **header** (see
85 | below for some discussion on ETag-like attributes in
86 | representations).
87 | * Requiring clients to send an If-Match header with a valid ETag when
88 | processing PUT requests.
89 | * Processing the If-Match header on the server side to compare the
90 | ETag provided in the request with the generated ETag of the
91 | currently stored representation. If there is a match, carry on
92 | with the request action, if not, respond with a 412 status code.
93 |
94 | .. note:: An ETag value is a double-quoted string: ``"the etag"``.
95 |
96 | .. note:: The If-Match header may contain multiple ETags (separated
97 | by commas). If it does, at least one must match for the
98 | request to proceed.
99 |
100 | .. note:: What section of a codebase takes the responsibility of
101 | managing the ETag and If-Match headers is greatly dependent on
102 | the architecture of the service. In general the handler or
103 | controller for each resource should be the locus of
104 | responsibility. It may be there are decorators or libraries
105 | that can be shared but such things are beyond the scope of
106 | this document. Early implementors are encouraged to write code
107 | that is transparent and easy to inspect, allowing easier
108 | future extraction.
109 |
110 | .. note:: ETags_ can be either strong or weak. see :rfc:`7232` for
111 | discussion on how weak ETags may be used. They are not
112 | addressed in this document as their import is primarily
113 | related to cache handling. Strong ETags signify
114 | byte-for-byte equivalence between representations of the
115 | same resource. Weak ETags indicate only semantic equivalence.
116 |
117 | Each of the steps listed above require functionality to generate ETags
118 | for representations. Whenever the representation is different the ETag
119 | should be different. :rfc:`7232#section-2.3.1` has advice on how to
120 | generate good ETags. In practice they should be:
121 |
122 | * Different for different forms of the same resource. For example, the
123 | XML and JSON representations of the same version of a resource
124 | should have different ETags.
125 | * Different from version to version.
126 | * Not based on something that will change when the system restarts.
127 | For example not be based on inodes or database keys that are ints
128 | or other non-universal identifiers.
129 | * Not be based on hashes of strings that do not have reliable
130 | ordering. For example it can be tempting to make md5 or sha hashes
131 | of the JSON string that represents a resource. If the ordering in
132 | that JSON is not guaranteed, the ETag is not useful.
133 |
134 | Ideally they should be fast to calculate or if not fast then easy
135 | to store (when the representation is written). A hash of a last
136 | udpated timestamp and the content-type can work, but only if updates
137 | are less frequent than clock updates.
138 |
139 | .. note:: Many details of how ETags can be useful are left out of this
140 | document. It is worth reading :rfc:`7232` in its entirety to
141 | understand their purpose, how they work, edge cases and
142 | how they interact with other modes of conditional request
143 | handling.
144 |
145 | Special Cases
146 | -------------
147 |
148 | For simple resources that represent a single unified entity the
149 | above handling works well. For more complex resources the situation
150 | becomes more complicated. Some scenarios worth considering:
151 |
152 | * When there is a resource which represents a collection of
153 | resources (e.g. ``GET /resources`` versus ``GET
154 | /resources/some-id``) the strict process for updating one of the
155 | resources in that collection when using ETags would be:
156 |
157 | * ``GET /resources`` to get the list of resources.
158 | * Do some client side processing to choose a singe resource's id.
159 | * ``GET /resources/that-id`` to get the resource and its ``ETag``
160 | header.
161 | * Modify the local representation.
162 | * ``PUT /resources/that-id`` with an ``If-Match`` header
163 | containing the ETag.
164 |
165 | This may be considered cumbersome. One way to optimize this is to
166 | include an attribute whose value is the ETag in the individual
167 | representations of the singular resources in the collection
168 | resource. Then the second GET above can be skipped as the ETag is
169 | already available.
170 |
171 | * When a resource has sub resources (e.g. an ``/image/id`` resource
172 | contains a metadata attribute whose content is also available at
173 | ``/image/id/metadata``) it can be desirable to retrieve the image
174 | resource and then PUT to the metadata resource. Strictly speaking
175 | this would require a GET of the metadata resource to determine the
176 | ETag.
177 |
178 | If this is a problem, an optimization to work around this is to
179 | allow the ETag of the image resource to be an acceptable ETag of
180 | the metadata resource when provided in an ``If-Match`` header.
181 | If this is done, then it is important that the reverse not be
182 | true: The ETag sent with the metadata resource should not be valid
183 | in an ``If-Match`` header sent to the image resource.
184 |
185 | .. note:: In both of the above scenarios the semantics of ETags are being
186 | violated. An ETag is not a magic key to unlock a resource and
187 | make it writable. It is a value used to determine if two
188 | representations of the same resource are in fact the same. In
189 | the situations above they are comparing different resources.
190 | Services should only do so if they must. Either because the
191 | performance benefit is huge (in which case consider fixing the
192 | performance of the API) or the user experience improvement is
193 | significant. The latter is far more important and legitimate
194 | than the former
195 |
196 | .. _lost update problem: https://www.w3.org/1999/04/Editing/
197 | .. _ETags: https://tools.ietf.org/html/rfc7232#section-2.3
198 | .. _412: https://tools.ietf.org/html/rfc7232#section-4.2
199 | .. _If-Match: https://tools.ietf.org/html/rfc7232#section-3.1
200 |
--------------------------------------------------------------------------------
/guidelines/evaluating_api_changes.rst:
--------------------------------------------------------------------------------
1 | ..
2 | This work is licensed under a Creative Commons Attribution 3.0 Unported
3 | License.
4 |
5 | http://creativecommons.org/licenses/by/3.0/legalcode
6 |
7 | ======================
8 | Evaluating API Changes
9 | ======================
10 |
11 | This document has been superseded by :doc:`api_interoperability` after
12 | the guidelines in this document were discovered to insufficiently
13 | capture the concepts of `stability` and `compatibility` in a world
14 | where `interoperability` between clouds is the ultimate priority.
15 |
--------------------------------------------------------------------------------
/guidelines/extensions.rst:
--------------------------------------------------------------------------------
1 | .. _extensions:
2 |
3 | API Extensions
4 | ==============
5 |
6 | This topic document serves to provide guidance on the topic of API extensions.
7 |
8 | See also the topic documents on :ref:`discoverability` and
9 | :ref:`interoperability`.
10 |
11 | Guidance
12 | --------
13 |
14 | API extensions are sometimes used to add custom functionality to single
15 | deployments of OpenStack. They are not recommended, because when they are
16 | used, they break interoperability between that cloud and other OpenStack
17 | deployments.
18 |
19 | If a deployment requires custom behaviors over an HTTP API it should be
20 | implemented in a service separate from the existing OpenStack services.
21 |
22 | Those projects that support different functionality based on the presence
23 | of drivers should strive to contain those differences to the values (not keys)
24 | in representation objects. Having different URLs in a service, based on
25 | different drivers, breaks interoperability. Where such functionality is
26 | absolutely required then it is critical that the functionality be discoverable
27 | via a capabilities API.
28 |
29 | .. note:: At this time the standards and best practices for capabilities
30 | discovery are undefined.
31 |
--------------------------------------------------------------------------------
/guidelines/general.rst:
--------------------------------------------------------------------------------
1 |
2 | General
3 | =======
4 |
5 | This topic document serves to provide guidance on general topics of
6 | style and approaches to developing and managing public HTTP APIs.
7 |
8 | Guidelines
9 | ----------
10 |
11 | 1. While every public API should have a canonical client
12 | implementation we should discourage tight bindings between an API
13 | and its clients and strongly encourage separate evolution of
14 | the API and its official client. We welcome any effort aimed at
15 | developing and promoting alternative clients. A diversity of
16 | implementations is healthy and enables use in many environments.
17 |
--------------------------------------------------------------------------------
/guidelines/headers.rst:
--------------------------------------------------------------------------------
1 | ..
2 | This work is licensed under a Creative Commons Attribution 3.0 Unported
3 | License.
4 |
5 | http://creativecommons.org/licenses/by/3.0/legalcode
6 | .. _headers:
7 |
8 | ======================
9 | HTTP Header Guidelines
10 | ======================
11 |
12 | Deprecated X-Foo Naming Scheme
13 | ------------------------------
14 |
15 | In :rfc:`6648` the recommendation to prefix application-specific headers with
16 | ``X-`` was retracted. It is mentioned in :rfc:`2616` as a permanently-reserved
17 | prefix for implementors, but is deprecated due to the complexities of migrating
18 | prefixed headers to standardized ones. This has resulted in some standards
19 | reserving X-prefixed names in addition to their non-prefixed headers. (see
20 | X-Archived-At/Archived-At) In the more recent :rfc:`7231#section-8.3.1`
21 | designers of new protocols are discouraged from using X-prefixed headers and to
22 | keep new headers short where possible.
23 |
24 | Guidance
25 | ********
26 | This **does not** mean it is recommended to replace existing uses of ``X-``, or
27 | in using ``X-`` in private/local/development contexts. New APIs (or new API
28 | features) should make their best effort to not use header names that conflict
29 | with other applications. To do this, use "OpenStack" and the service name in
30 | the header. An example might be "OpenStack-Compute-FooBar", which is unlikely
31 | to be standardized already or conflict with existing headers.
32 |
33 | :rfc:`6648` intentionally does not disallow using ``X-`` as a prefix, but does
34 | remove the experimental/unstandardized semantics from the prefix. For
35 | existing projects, it is acceptable to create new headers prefixed with X
36 | since it is likely that the rest of the headers already standardized in the API
37 | begin with ``X-``.
38 |
39 | Examples
40 | ********
41 |
42 | Some good header names that are clear, unlikely to conflict, and could become
43 | standardized might be ``OpenStack-Identity-Status`` Some headers that are at
44 | risk for conflicts might look like::
45 |
46 | Account-ID
47 | Host-Name
48 | Storage-Policy
49 |
50 | In these cases, adding ``OpenStack-`` as a prefix resolves the ambiguity, as
51 | in::
52 |
53 | OpenStack-Identity-Account-ID
54 | OpenStack-Networking-Host-Name
55 | OpenStack-Object-Storage-Policy
56 |
57 | Avoid Proliferating Headers
58 | ---------------------------
59 |
60 | It can be tempting to use the names of headers as a way of passing
61 | specific information between the client and the server. Where possible
62 | this should be avoided in favor of using a more generic header name
63 | and placing the specifics in the value. For example compare the
64 | following two headers::
65 |
66 | OpenStack-API-Version: compute 2.1
67 | OpenStack-Nova-API-Version: 2.1
68 |
69 | .. note:: The first header is the recommended form. The second header
70 | is in the form of a microversion header currently in use. It
71 | effectively demonstrates the problem. Also note that whereas the
72 | second header uses a service name, the first header uses the more
73 | correct service type.
74 |
75 | At first glance these header name and value pairs convey the same
76 | information, with the second option being a bit easier to parse on
77 | the server side. However consider the following problems when using
78 | the second form:
79 |
80 | * A new header is needed every time there is a new service.
81 | * It violates the principle that in key-value based data structures
82 | the key should be an accessor only, that is: It should be opaque
83 | and generic.
84 | * If CORS [1]_ middleware is being used, it needs to be configured to
85 | allow a multitude of headers instead of a generic one.
86 | * Generic library code (in either the client or the server) that is
87 | supposed to deal with this class of header has to construct or parse
88 | strings on both sides of the name-value pair.
89 |
90 | .. [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
91 |
--------------------------------------------------------------------------------
/guidelines/http.rst:
--------------------------------------------------------------------------------
1 | .. _http:
2 |
3 | HTTP Guidelines
4 | ===============
5 |
6 | The HTTP RFC is a quite large specification. The HTTP 1.1
7 | specification :rfc:`2616` clocks in at 175 pages. Published in
8 | 1999 it assumes a certain use of the HTTP protocol in the web browser
9 | / server framework. The idea for the use of HTTP as a more generic API
10 | layer only emerged a year after the publication of HTTP 1.1 in
11 | `Chapter 5 of Roy Fieldings PhD thesis
12 | `_
13 | and was not widely adopted until many years later.
14 |
15 | It's important to realize that concepts and constructs that we want to
16 | manipulate in any given system will not be a perfect match with the
17 | concepts and constraints of HTTP. These mismatches can indicate a
18 | clearly wrong usage of HTTP, a special case to meet requirements, or
19 | an opportunity to improve an existing design. Any recommendation about
20 | using HTTP should come with substantial explanation about why that's
21 | the best approach that we can determine at the time. This provides the
22 | justification for the decision in the present, and bread crumbs in the
23 | future if that justification no longer holds.
24 |
25 | HTTP defines a set of standard headers, content negotiation with mime
26 | types, well defined status codes, a url structure, and methods on such
27 | urls.
28 |
29 | If something is not covered by this document, or seems ambiguous after
30 | looking at these guidelines, implementers are encouraged to start a
31 | mailing list thread (with references to what they believe are relevant
32 | RFC sections) to clarify and to help make these guidelines more clear
33 | in the future. However, like legal code, an RFC is only a starting
34 | point. Precedents and common usage shape what an active standard
35 | really means.
36 |
37 | *Note:* in recent years :rfc:`2616` was split into a multipart
38 | document in :rfc:`7230`, :rfc:`7231`, :rfc:`7232`, :rfc:`7233`,
39 | :rfc:`7234`, and :rfc:`7235`. No major functional changes are in
40 | these documents, but they are just reorganized for readability, and
41 | small clarifying corrections were made.
42 |
43 | Guidelines
44 | ----------
45 |
46 | .. toctree::
47 | :maxdepth: 2
48 |
49 | http/methods
50 | http/response-codes
51 | headers
52 | links
53 | http/caching
54 |
--------------------------------------------------------------------------------
/guidelines/http/caching.rst:
--------------------------------------------------------------------------------
1 | HTTP Caching and Proxy Behavior
2 | ===============================
3 |
4 | HTTP was designed to be proxied and cached heavily. HTTP caching by
5 | both intermediary proxies, and by clients themselves, is to be
6 | expected in all cases where it is allowed. This is a fundamental
7 | design point to allow HTTP to work at high scale.
8 |
9 | That means that whenever a response is defined as cacheable, for any
10 | reason, the server implementation should assume that those responses
11 | will be cached. This could mean that the server **will never see**
12 | follow up requests if it does not specify appropriate Cache-Control
13 | directives on cacheable responses.
14 |
15 | The following HTTP methods are defined as cacheable: HEAD, GET, and
16 | POST :rfc:`7231#section-4.2.3` (section 4.2.3).
17 |
18 | Requests that return a status code of any of the following are defined
19 | as cacheable: 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and
20 | 501 :rfc:`7231#section-6.1` (section 6.1).
21 |
22 | A common misconception is that requests issued over a secure HTTP connection
23 | are not cached for security reasons. In fact, there is no exception made for
24 | https in the HTTP specification, caching works in exactly the same way as for
25 | non-encrypted HTTP. Most modern browsers apply the same caching algorithm to
26 | secure connections.
27 |
28 | Most Python HTTP client libraries are extremely conservative on
29 | caching, so a whole class of completely valid RFC caching won't be
30 | seen when using these clients. Assuming "it works in the Python
31 | toolchain" does not mean that it will in all cases, or is the only way
32 | to implement the HTTP. We expect that in-browser javascript clients
33 | will have vastly different cache semantics (that are completely valid
34 | by the RFC) than the existing Python clients.
35 |
36 | Thinking carefully about cache semantics when implementing anything
37 | in the OpenStack API is critical to the API being compatible with the
38 | vast range of runtimes, programming languages, and proxy servers (open
39 | and commercial) that exist in the wild.
40 |
41 | Cache Headers in Practice
42 | ~~~~~~~~~~~~~~~~~~~~~~~~~
43 |
44 | Given what is said above ("caching [...] is to be expected in all cases"),
45 | services MUST provide appropriate ``Cache-Control`` headers to avoid bugs like
46 | those described in
47 | `1747935 `_ wherein
48 | an intermediary proxy caches a response indefinitely, despite a change in the
49 | underlying resource.
50 |
51 | To avoid this problem, at a minimum, responses defined above as "cacheable"
52 | that do not otherwise control caching MUST include a header of::
53 |
54 | Cache-Control: no-cache
55 |
56 | Despite how it sounds, ``no-cache`` (defined by :rfc:`7234#section-5.2.1.4`)
57 | means only use a cached resource if it can be validated against the origin
58 | server. However, in the absence of headers which can be sent back to the
59 | server in an ``If-Modified-Since`` or ``If-None-Match`` conditional request,
60 | ``no-cache`` means no caching will happen. For more on validation see
61 | :rfc:`7234#section-4.3`.
62 |
63 | This means that at least all responses to ``GET`` requests that return a
64 | ``200`` status need the header, unless explicit caching requirements are
65 | expressed in the response.
66 |
67 | MDN provides a good overview of the `Cache-Control header
68 | `_ and
69 | provides some guidance on ways to indicate that caching is desired. If caching
70 | is expected, in addition to the ``Cache-Control`` header, headers such as
71 | ``ETag`` or ``Last-Modified`` must also be present.
72 |
73 | Describing how to do cache validation and conditional request handling is out
74 | of scope for these guidelines because the requirements will be different from
75 | service to service.
76 |
--------------------------------------------------------------------------------
/guidelines/http/methods.rst:
--------------------------------------------------------------------------------
1 | HTTP Methods
2 | ============
3 |
4 | HTTP defines a concept of METHODS on a resource URI.
5 |
6 | ..
7 |
8 | +-------------+--------------+--------------------+--------------------+
9 | | METHOD | URI | ACTION | HAS REQUEST BODY? |
10 | +-------------+--------------+--------------------+--------------------+
11 | | HEAD | /foo/ID | EXISTS | NO |
12 | +-------------+--------------+--------------------+--------------------+
13 | | GET | /foo/ID | READ | NO |
14 | +-------------+--------------+--------------------+--------------------+
15 | | POST | /foo | CREATE | YES |
16 | +-------------+--------------+--------------------+--------------------+
17 | | PUT | /foo/ID | UPDATE | YES |
18 | +-------------+--------------+--------------------+--------------------+
19 | | PATCH | /foo/ID | UPDATE (partial) | YES |
20 | +-------------+--------------+--------------------+--------------------+
21 | | DELETE | /foo/ID | DELETE | NO |
22 | +-------------+--------------+--------------------+--------------------+
23 |
24 | The mapping of HTTP requests method to the Create, Read, Update, Delete
25 | (`CRUD
26 | `_) model
27 | is one of convenience that can be considered a useful, but incomplete,
28 | memory aid. Specifically it misrepresents the meaning and purpose
29 | of POST. According to :rfc:`7231#section-4.3.3` POST "requests that
30 | the target resource process the representation enclosed in the request
31 | according to the resource's own specific semantics". This can, and
32 | often does, mean create but it can mean many other things, based on
33 | the resource's requirements.
34 |
35 | More generally, CRUD models the four basic functions of persistent
36 | storage. An HTTP API is not solely a proxy for persistent storage.
37 | It can provide access to such storage, but it can do much more.
38 |
39 | Please note that while HEAD is recommended for checking for the existence of a
40 | resource, the corresponding GET should always be implemented too, and should
41 | return an identical response with the addition of a body, if applicable.
42 |
43 | **TODO**: HEAD is weird in a bunch of our wsgi frameworks and you
44 | don't have access to it. Figure out if there is anything useful
45 | there.
46 |
47 | **TODO**: Provide guidance on what HTTP methods (PUT/POST/PATCH/DELETE, etc)
48 | should always be supported, and which should be preferred.
49 |
50 | * When choosing how to update a stored resource, **PUT** and **PATCH** imply
51 | different semantics. **PUT** sends a full resource representation (including
52 | unchanged fields) which will replace the resource stored on the server. In
53 | contrast, **PATCH** accepts partial representation which will modify the
54 | server's stored resource. :rfc:`5789` does not specify a partial
55 | representation format. JSON-patch in :rfc:`6902` specifies a way to send a
56 | series of changes represented as JSON. One unstandardized alternative is to
57 | accept missing resource fields as unchanged from the server's saved state of
58 | the resource. :rfc:`5789` doesn't forbid using PUT in this way, but this
59 | method makes it possible to lose updates when dealing with lists or sets.
60 |
61 | * There can also be confusion on when to use **POST** or **PUT** in the
62 | specific instance of creating new resources. **POST** should be used when
63 | the URI of the resulting resource is different from the URI to which the
64 | request was made and results in the resource having an identifier (the URI)
65 | that the server generated. In the OpenStack environment this is the common
66 | case. **PUT** should be used for resource creation when the URI to which the
67 | request is made and the URI of the resulting resource is the same.
68 |
69 | That is, if the id of the resource being created is known, use **PUT** and
70 | **PUT** to the correct URI of the resource. Otherwise, use **POST** and
71 | **POST** to a more generic URI which will respond with the new URI of the
72 | resource.
73 |
74 | * The **GET** method should only be used for retrieving representations of
75 | resources. It should never change the state of the resource identified by
76 | the URI nor the state of the server in general. :rfc:`7231#section-4.3.1`
77 | states **GET is the primary mechanism of information retrieval and the
78 | focus of almost all performance optimizations.**.
79 |
80 | HTTP request bodies are theoretically allowed for all methods except TRACE,
81 | however they are not commonly used except in PUT, POST and PATCH. Because of
82 | this, they may not be supported properly by some client frameworks, and you
83 | should not allow request bodies for GET, DELETE, TRACE, OPTIONS and HEAD
84 | methods.
85 |
--------------------------------------------------------------------------------
/guidelines/http/response-codes.rst:
--------------------------------------------------------------------------------
1 | HTTP Response Codes
2 | ===================
3 |
4 | HTTP defines a set of standard response codes on requests, they are
5 | largely grouped as follows:
6 |
7 | * 1xx: compatibility with older HTTP, typically of no concern
8 | * 2xx: success
9 | * 3xx: redirection (the resource is at another location, or is
10 | unchanged since last requested)
11 | * 4xx: client errors (the client did something wrong)
12 | * 5xx: server errors (the server failed in a way that was unexpected)
13 |
14 | 2xx Success Codes
15 | ~~~~~~~~~~~~~~~~~
16 |
17 | * Synchronous resource creation
18 |
19 | * Response status code must be ``201 Created``
20 | * Must return a Location header with the URI of the created resource
21 | * Should return a representation of the resource in the body
22 |
23 | * Asynchronous resource creation
24 |
25 | * Response status code must be ``202 Accepted``
26 | * Must return a Location header set to one of the following:
27 | * the URI of the resource to be created, if known.
28 | * the URI of a status resource that the client can use to query the
29 | progress of the asynchronous operation.
30 |
31 | * Synchronous resource deletion
32 |
33 | * Response status code must be ``204 No Content``
34 |
35 | * For all other successful requests, the return code should be **200 OK**.
36 |
37 | * If a request attempts to put a resource into a state which it is
38 | already in (for example, locking an instance which is already locked), the
39 | return code should be in the **2xx Successful** range (usually matching the
40 | return code which would be given if the state had changed). It is not
41 | appropriate to use **409 Conflict** when the resulting state of the resource
42 | is as the user requested.
43 |
44 | 5xx Server Error Codes
45 | ~~~~~~~~~~~~~~~~~~~~~~
46 |
47 | These codes represent that the server, or gateway, has encountered an error
48 | or is incapable of performing the requested method. They indicate to a
49 | client that the request has resulted in an error that exists on the
50 | server side and not with the client.
51 |
52 | They should be used to indicate that errors have occurred during the
53 | request process which cannot be resolved by the client alone. The nature
54 | of each code in the 5xx series carries a specific meaning and they should
55 | be fully researched before deploying.
56 |
57 | The server **must not** return server-side stacktraces/traceback output to the
58 | end user. Tracebacks and stacktraces belong in server-side logs, not returned
59 | via the HTTP API to an end user.
60 |
61 | Failure Code Clarifications
62 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
63 |
64 | * If the request results in the OpenStack user exceeding his or her quota, the
65 | return code should be **403 Forbidden**. Do **not** use **413 Request
66 | Entity Too Large**.
67 |
68 | * For badly formatted requests, the return code should be **400 Bad Request**.
69 | Do **not** use **422 Unprocessable Entity**.
70 |
71 | * If the API limits the length of a property that is a collection, the return
72 | code should be **400 Bad Request** when the request exceeds the length
73 | limit. The client should adjust requests to achieve success, and shouldn't
74 | expect to repeat the request and have it work. Do **not** use
75 | **403 Forbidden** for this case, because this is different than exceeding
76 | quota -- for a subsequent request to succeed when quotas are exceeded the
77 | server environment must change.
78 |
79 | * If a request contains a reference to a nonexistent resource in the body
80 | (not URI), the code should be **400 Bad Request**. Do **not** use **404
81 | NotFound** because :rfc:`7231#section-6.5.4` (section 6.5.4) mentions **the
82 | origin server did not find a current representation for the target resource**
83 | for 404 and **representation for the target resource** means a URI. A good
84 | example of this case would be when requesting to resize a server to a
85 | non-existent flavor. The server is the resource in the URI, and as long as it
86 | exists, 404 would never be the proper response. **422 Unprocessable Entity**
87 | is also an option for this situation but do **not** use 422 because the code
88 | is not defined in :rfc:`7231` and not standard. Since the 400 response code
89 | can mean a wide range of things, it is extremely important that the error
90 | message returned clearly indicates that the resource referenced in the body
91 | does not exist, so that the consumer has a clear understanding of what they
92 | need to do to correct the problem.
93 |
94 | * If a request contains an unexpected attribute in the body, the server should
95 | return a **400 Bad Request** response. Do **not** handle the request as
96 | normal by ignoring the bad attribute. Returning an error allows the client
97 | side to know which attribute is wrong and have the potential to fix a bad
98 | request or bad code. (For example, `additionalProperties` should be `false`
99 | on JSON-Schema definition)
100 |
101 | * Similarly, if the API supports query parameters and a request contains an
102 | unknown or unsupported parameter, the server should return a **400 Bad
103 | Request** response. Invalid values in the request URL should never be
104 | silently ignored, as the response may not match the client's expectation. For
105 | example, consider the case where an API allows filtering on name by
106 | specifying '?name=foo' in the query string, and in one such request there is
107 | a typo, such as '?nmae=foo'. If this error were silently ignored, the user
108 | would get back all resources instead of just the ones named 'foo', which
109 | would not be correct. The error message that is returned should clearly
110 | indicate the problem so that the user could correct it and re-submit.
111 |
112 | * If a request is made to a known resource URI, but the HTTP method used for
113 | the request is not supported for that resource, the return code should be
114 | **405 Method Not Allowed**. The response should include the `Allow` header
115 | with the list of accepted request methods for the resource.
116 |
117 | * If a request is made which attempts to perform an action on a resource which
118 | is already performing that action and therefore the request cannot be
119 | fulfilled (for example, snapshotting an instance which is already in the
120 | process of snapshotting), the return code should be **409 Conflict**.
121 |
122 | * A **500 Internal Server Error** should **not** be returned to the user for
123 | failures due to user error that can be fixed by changing the request on the
124 | client side. 500 failures should be returned for any error state that cannot
125 | be fixed by a client, and requires the operator of the service to perform
126 | some action to fix. It is also possible that this error can be raised
127 | deliberately in case of some detected but unrecoverable error such as a
128 | MessageQueueTimeout from a failure to communicate with another service
129 | component, an IOError caused by a full disk, or similar error.
130 |
131 | .. note:: If an error response body is returned, it must conform to the
132 | :ref:`errors` guideline.
133 |
134 | Common Mistakes
135 | ---------------
136 |
137 | There are many common mistakes that have been made in the
138 | implementations of RESTful APIs in OpenStack. This section attempts to
139 | enumerate them with reasons why they were wrong, and propose future
140 | alternatives.
141 |
142 | Use of 501 - Not Implemented
143 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144 |
145 | Some time in the Folsom era projects started using 501 for "Feature
146 | Not Implemented" - `Discussion on openstack-dev
147 | `_
148 |
149 | This is a completely incorrect reading of HTTP. "Method" means
150 | something very specific in HTTP, it means an HTTP Method. One of GET /
151 | HEAD / POST / PUT / PATCH / OPTIONS / TRACE.
152 |
153 | The purpose of the 501 error was to indicate to the client that POST
154 | is not now, and never will be an appropriate method to call on any
155 | resource on the server. An appropriate client action is to blacklist
156 | POST and ensure no code attempts to use this. This comes from the
157 | early days of HTTP where there were hundreds of commercial HTTP server
158 | implementations, and the assumption that all HTTP methods would be
159 | handled by a server was not something the vendors could agree on. This
160 | usage was clarified in RFC :rfc:`7231#section-6.6.2` (section 6.6.2).
161 |
162 | If we assume the following rfc statement to be true: "This is the
163 | appropriate response when the server does not recognize the request
164 | method and is not capable of supporting it for any resource." that is
165 | irreconcilable with a narrower reading, because we've said all clients
166 | are correct in implementing "never send another POST again to any
167 | resource". It's as if saying the "closed" sign on a business means
168 | both, closed for today, as well as closed permanently and ok for the
169 | city to demolish the building tomorrow. Stating that either is a valid
170 | reading so both should be allowed only causes tears and confusion.
171 |
172 | We live in a very different world today, dominated by Apache and
173 | Nginx. As such 501 is something you'd be unlikely to see in the
174 | wild. However that doesn't mean we can replace it's definition with
175 | our own.
176 |
177 | Going forward projects should use a 400 'BadRequest' response for this
178 | condition, plus a more specific error message back to the user that
179 | the feature was not implemented in that cloud. 404 'NotFound' may also
180 | be appropriate in some situations when the URI will never
181 | exist. However one of the most common places where we would return
182 | "Feature Not Implemented" is when we POST an operation to a URI of the
183 | form /resource/{id}/action. Clearly that URI is found, however some
184 | operations on it were not supported. Returning a 404 (which is by
185 | default cachable) would make the client believe /resource/{id}/action
186 | did not exist at all on the server.
187 |
--------------------------------------------------------------------------------
/guidelines/links.rst:
--------------------------------------------------------------------------------
1 | .. _links:
2 |
3 | Links
4 | =====
5 |
6 | Links to other resources often need to be represented in responses. There is
7 | already a well established format for this representation in `JSON
8 | Hyper-Schema: Hypertext definitions for JSON Schema
9 | `_.
10 | This is already the `prevailing representation
11 | `_ in
12 | use by a number of prominent OpenStack projects and also in use by the
13 | :ref:`errors` guideline.
14 |
15 | .. note:: Before inventing a new value for ``rel``, please check the existing
16 | `Link Relations
17 | `_ for
18 | something you can reuse.
19 |
20 | Links Example
21 | -------------
22 |
23 | .. code-block:: javascript
24 |
25 | {
26 | "links": [
27 | {
28 | "rel": "help",
29 | "href": "http://developer.openstack.org/api-ref/compute/#create-server"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/guidelines/metadata.rst:
--------------------------------------------------------------------------------
1 | .. _metadata:
2 |
3 | Metadata
4 | ========
5 |
6 | This topic document serves to provide guidance on how to work with metadata
7 | in OpenStack REST APIs.
8 |
9 | Metadata is sometimes confused with tags. While they have some things in
10 | common, the main function of metadata is to attach additional information,
11 | in the form of key-value pairs, to entities. Tags, on the other side, are
12 | used to classify entities in groups. A separate guidelines document exists
13 | for tags.
14 |
15 | For background on the REST guidelines referenced here, see the topic documents
16 | on :ref:`naming` and :ref:`http`.
17 |
18 | Metadata Representation
19 | -----------------------
20 |
21 | A Python dictionary is used as representation of metadata for a resource. This
22 | dictionary is added as an additional field in the representation of the parent
23 | resource with name ``metadata``.
24 |
25 | Example request using a server resource::
26 |
27 | GET /servers/1234567890
28 |
29 | Response::
30 |
31 | {
32 | 'id': '1234567890',
33 |
34 | ... other server resource properties ...
35 |
36 | 'metadata': {
37 | "foo": "Foo Value",
38 | "bar": "Bar Value",
39 | "baz": "Baz Value"
40 | }
41 | }
42 |
43 | Updates to the metadata are issued in accordance to the standard HTTP request
44 | methods, issued directly against the parent resource, so for example, to
45 | update the metadata dictionary of a resource, a PUT request should be sent to
46 | the resource, including not only the metadata, but the complete resource
47 | representation in the body. The update in this case does not need to be limited
48 | to metadata, other properties can be updated at the same time.
49 |
50 | For resources that have a representation that is not in JSON, a separate
51 | endpoint must be created to expose the metadata. See the "Metadata Resource
52 | URLs" section below for more information.
53 |
54 | Character Encoding for Metadata Keys and Values
55 | -----------------------------------------------
56 |
57 | Per :rfc:`7159#section-8.1`, JSON documents shall be encoded in UTF-8, UTF-16,
58 | or UTF-32, with UTF-8 being the default and the recommended encoding for
59 | maximum interoperability.
60 |
61 | Since the entire metadata representation is a JSON document, the encoding of
62 | the keys and values must match the encoding of the parent document. The use
63 | of UTF-8 encoding is strongly recommended.
64 |
65 | Metadata Resource URLs
66 | ----------------------
67 |
68 | Sometimes it may be inconvenient to work with the metadata portion of a
69 | resource using the complete resource representation, so metadata can also be
70 | exposed as a stand-alone resource. The root resource URL for metadata
71 | management must be the URL of the resource to which the metadata belongs,
72 | followed by */metadata* (for APIs that use user-generated URLs with varying
73 | number of components the */metadata* URL component can be added as a prefix
74 | instead of a suffix).
75 |
76 | For example, the resource identified by URL
77 | *http://example.com:8774/servers/1234567890* must expose its metadata with
78 | root URL *http://example.com:8774/servers/1234567890/metadata*.
79 |
80 | Obtaining Metadata
81 | ~~~~~~~~~~~~~~~~~~
82 |
83 | To obtain the metadata for a resource, a GET request must be sent to the root
84 | metadata URL. On success, the server responds with a 200 status code and the
85 | complete set of metadata items in the response body.
86 |
87 | Example request::
88 |
89 | GET /servers/1234567890/metadata
90 |
91 | Response::
92 |
93 | {
94 | "metadata": {
95 | "foo": "Foo Value",
96 | "bar": "Bar Value",
97 | "baz": "Baz Value"
98 | }
99 | }
100 |
101 | Modifying Metadata
102 | ~~~~~~~~~~~~~~~~~~
103 |
104 | To add, remove, or change metadata items, a PUT request must be sent to the
105 | root metadata URL, with the updated complete list of metadata items in the
106 | body of the request. On success, the server responds with a 200 status code
107 | and the complete updated metadata block in the response body.
108 |
109 | .. note:: A PUT request should use etags to avoid the lost update problem.
110 |
111 | Example request (updates "foo", removes "bar", adds "qux" and leaves "baz"
112 | untouched)::
113 |
114 | PUT /servers/1234567890/metadata
115 | {
116 | "metadata": {
117 | "foo": "Foo Value Updated",
118 | "baz": "Baz Value",
119 | "qux": "Qux Value"
120 | }
121 | }
122 |
123 | Response::
124 |
125 | {
126 | "metadata": {
127 | "foo": "Foo Value Updated",
128 | "baz": "Baz Value",
129 | "qux": "Qux Value"
130 | }
131 | }
132 |
133 | Deleting Metadata
134 | ~~~~~~~~~~~~~~~~~
135 |
136 | To delete the entire metadata block associated with a resource, a DELETE
137 | request must be sent to the root metadata URL. On success, the server responds
138 | with a 204 status code.
139 |
140 | Example request::
141 |
142 | DELETE /servers/1234567890/metadata
143 |
144 | To delete multiple metadata items without affecting the remaining ones,
145 | a PUT request must be sent to the root metadata URL with the updated complete
146 | list of metadata items (without items to delete) in the body of the request.
147 | On success, the server responds with a 200 status code.
148 |
149 | Example request (removes “foo” and “qux”)::
150 |
151 | PUT /servers/1234567890/metadata
152 | {
153 | "metadata": {
154 | "baz": "Baz Value"
155 | }
156 | }
157 |
158 | Response::
159 |
160 | {
161 | "metadata": {
162 | "baz": "Baz Value"
163 | }
164 | }
165 |
166 | To delete a single metadata item see below.
167 |
168 | Addressing Individual Metadata Items
169 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170 |
171 | As an optional extension to the above, an API can elect to expose additional
172 | endpoints to give clients the ability to work with individual metadata items.
173 | If a project decides to implement this option, then each metadata key-value
174 | pair should be accessed individually at a URL formed by appending the key name
175 | to the root metadata URL. Note that this option is not available for APIs that
176 | use user-generated URLs.
177 |
178 | To insert a single metadata item without having to send the entire metadata
179 | block, the client can send a POST request to the root metadata URL, and
180 | include the individual metadata item representation in the request body. On
181 | success, the server responds with a 201 status code and includes the new
182 | metadata item's URL in the ``Location`` header in the response.
183 |
184 | Example request::
185 |
186 | POST /servers/1234567890/metadata
187 | {
188 | "key": "qux",
189 | "value": "Qux Value"
190 | }
191 |
192 | Response::
193 |
194 | Location: http://example.com:8774/servers/1234567890/metadata/qux
195 | {
196 | "key": "qux",
197 | "value": "Qux Value",
198 | }
199 |
200 | As shown in the above example, metadata items can be accessed individually by
201 | appending the key name to the root metatadata URL. The representation includes
202 | the key and the value. This format gives APIs the option to include additional
203 | properties that describe a metadata item, such as an expiration date.
204 |
205 | To modify an item, a PUT request is sent to the metadata item's URL. On
206 | success, the server responds with a 200 status code and the updated
207 | representation of the metadata item in the response body.
208 |
209 | Example request::
210 |
211 | PUT /servers/1234567890/metadata/qux
212 | {
213 | "key": "qux",
214 | "value": "Qux Value Updated"
215 | }
216 |
217 | Response::
218 |
219 | {
220 | "key": "qux",
221 | "value": "Qux Value Updated"
222 | }
223 |
224 | To delete a single metadata item without affecting the remaining ones, a
225 | DELETE request is sent to the metadata item URL. On success, the server
226 | responds with a 204 status code.
227 |
228 | Example request::
229 |
230 | DELETE /servers/1234567890/metadata/qux
231 |
--------------------------------------------------------------------------------
/guidelines/microversion-errors-example.json:
--------------------------------------------------------------------------------
1 | {
2 | "errors": [
3 | {
4 | "request_id": "2ee92f06-8ede-4fb4-8921-b507601fb59d",
5 | "code": "compute.microverion-unsupported",
6 | "status": 406,
7 | "title": "Requested microversion is unsupported",
8 | "detail": "Version 5.3 is not supported by the API. Minimum is 2.1 and maximum is 5.2.",
9 | "max_version": "5.2",
10 | "min_version": "2.1",
11 | "links": [
12 | {
13 | "rel": "help",
14 | "href": "http://developer.openstack.org/api-guide/compute/microversions.html"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/guidelines/microversion_specification.rst:
--------------------------------------------------------------------------------
1 | .. _microversion_specification:
2 |
3 | Microversion Specification
4 | ==========================
5 |
6 | This topic document serves to provide guidance on how to work with
7 | microversions in OpenStack REST APIs.
8 |
9 | Microversions enables the ability to introduce API changes while being able
10 | to allow clients to discover those changes. According to negotiations with
11 | servers, clients adjust their behavior to work with server correctly.
12 |
13 | .. _versioning:
14 |
15 | Versioning
16 | ----------
17 |
18 | Versioning of the API should be single monotonic counter taking the form
19 | X.Y following these conventions:
20 |
21 | * X should only be changed if a significant backwards incompatible
22 | API change is made which affects the API as whole. That is, something
23 | that is only very rarely incremented. X should be changed in the following
24 | cases.
25 |
26 | * A number of endpoints get replaced by others
27 | * Drastic changes in API consumer workflow
28 |
29 | * Y should be changed when you make any change to the API. Note that this
30 | includes semantic changes which may not affect the input or output formats or
31 | even originate in the API code layer. We are not distinguishing
32 | between backwards compatible and backwards incompatible changes in
33 | the versioning system. It will however be made clear in the
34 | documentation as to what is a backwards compatible change and what
35 | is a backwards incompatible one.
36 |
37 | .. note:: Note that these versions numbers do not follow the rules of
38 | `Semantic Versioning `_.
39 |
40 | There are minimum and maximum versions which are used to describe what the
41 | server can understand. The minimum and maximum versions are the oldest and
42 | most recent versions supported. So clients can specify different version in
43 | the supporting range for each API call on the same server. The minimum version
44 | can be increased when supporting old clients is too great a burden.
45 | Increasing the minimum version means breaking the old clients, this happens
46 | very rarely also.
47 |
48 | Services expose information about their minimum and maximum supported
49 | microversion range as part of :ref:`version-discovery`.
50 |
51 | Each version includes all the changes since minimum version was introduced.
52 | It is not possible to request the feature introduced at microversion X.Y
53 | without accepting all the changes before X.Y in the same request.
54 | For example, you cannot request the feature which was introduced at
55 | microversion 2.100 without backwards incompatible changes which were
56 | introduced in microversion 2.99 and earlier.
57 |
58 | .. _microversion-client-interaction:
59 |
60 | Client Interaction
61 | ------------------
62 |
63 | A client specifies the version of the API they want via the following
64 | approach, a new header::
65 |
66 | OpenStack-API-Version: [SERVICE_TYPE] [VERSION_STRING]
67 |
68 | For example, Keystone will use the header::
69 |
70 | OpenStack-API-Version: identity 2.114
71 |
72 | This conceptually acts like the accept header.
73 |
74 | Clients should expect the following behavior from the server:
75 |
76 | * If the OpenStack-API-Version header is not provided, act as if
77 | the minimum supported version was specified.
78 |
79 | * If the OpenStack-API-Version header is sent, but the value does
80 | not match the current service, act as if the minimum supported
81 | version was specified.
82 |
83 | * If the OpenStack-API-Version header is sent, the service type
84 | matches, and the version takes the form of a version string respond
85 | with the API at the indicated version. If the version is outside the
86 | range of versions supported and is not the string ``latest`` (as
87 | described below), return 406 Not Acceptable, and a response body
88 | including the supported minimum and maximum versions.
89 |
90 | * The value of ``OpenStack-API-Version`` header is
91 | ``[SERVICE_TYPE] [VERSION_STRING]``. The VERSION_STRING must match
92 | `"^([1-9]\d*)\.([1-9]\d*|0)$"`. If the VERSION_STRING doesn't match the regex
93 | pattern, return a 400 Bad Request with an error response body that conforms
94 | to the errors guideline :ref:`errors`.
95 |
96 | * If OpenStack-API-Version header is sent, the service type matches, and
97 | the version is set to the special keyword ``latest`` behave as if
98 | maximum version was specified.
99 |
100 | .. warning:: The ``latest`` value is mostly meant for integration testing and
101 | would be dangerous to rely on in client code since microversions are not
102 | following `semver `_ and therefore backward compatibility
103 | is not guaranteed. Clients should always require a specific microversions but
104 | limit what is acceptable to the version range that it understands at the
105 | time.
106 |
107 | Two extra headers are always returned in the response::
108 |
109 | OpenStack-API-Version: [SERVICE_TYPE] version_number
110 | Vary: OpenStack-API-Version
111 |
112 | The first header specifies the version number of the API which was
113 | executed. An example::
114 |
115 | OpenStack-API-Version: compute 2.22
116 |
117 | The ``Vary`` header is used as a hint to caching proxies and user
118 | agents that the response is also dependent on the OpenStack-API-Version
119 | and not just the body and query parameters. See
120 | :rfc:`7231#section-7.1.4` for details.
121 |
122 | .. note:: Servers must be prepared to deal with multiple
123 | OpenStack-API-Version headers. This could happen when a client
124 | designed to address multiple services always sends the headers it
125 | thinks it needs. Most Python frameworks will handle this by setting
126 | the value of the header to the values of all matching headers,
127 | joined by a ',' (comma). For example ``compute 2.11,identity
128 | 2.114``.
129 |
130 | .. note:: A Python library called `microversion-parse`_ is available
131 | to help with server-side processing of microversion
132 | headers, both the new style described in this document and
133 | previous forms.
134 |
135 | .. _microversion-parse: https://pypi.org/project/microversion_parse
136 |
137 | When the requested version is out of range for the server, the server returns
138 | status code **406 Not Acceptable** along with a response body.
139 |
140 | The error response body conforms to the errors guideline :ref:`errors` with
141 | two additional properties as described in the json-schema below:
142 |
143 | ::
144 |
145 | {
146 | "max_version": {
147 | "type": "string", "pattern": "^([1-9]\d*)\.([1-9]\d*|0)$"
148 | },
149 | "min_version": {
150 | "type": "string", "pattern": "^([1-9]\d*)\.([1-9]\d*|0)$"
151 | }
152 | }
153 |
154 | An example HTTP Header response:
155 |
156 | ::
157 |
158 | HTTP/1.1 406 Not Acceptable
159 | Openstack-API-Version: compute 5.3
160 | Vary: OpenStack-API-Version
161 |
162 | An example errors body response:
163 |
164 | .. literalinclude:: microversion-errors-example.json
165 | :language: json
166 |
--------------------------------------------------------------------------------
/guidelines/naming.rst:
--------------------------------------------------------------------------------
1 | .. _naming:
2 |
3 | Naming Conventions
4 | ==================
5 |
6 | This topic document serves to provide guidance on how to name resources in
7 | OpenStack public REST APIs so that our APIs feel consistent and professional.
8 |
9 | REST API resource names
10 | -----------------------
11 |
12 | * A resource in a REST API is always represented as the plural of an entity
13 | that is exposed by the API.
14 |
15 | * Resource names exposed in a REST API should use all lowercase characters.
16 |
17 | * Resource names *may* include hyphens.
18 |
19 | * Resource names should *not* include underscores or other punctuation
20 | (sole exception is the hyphen).
21 |
22 | Fields in an API request or response body
23 | -----------------------------------------
24 |
25 | HTTP requests against an API may contain a body which is typically a serialized
26 | representation of the resource that the user wished to create or modify.
27 | Similarly, HTTP responses contain a body that is usually the serialized
28 | representation of a resource that was created, modified, or listed by the
29 | server.
30 |
31 | Fields within these serialized request and response bodies should be named
32 | according to these guidelines:
33 |
34 | * Field names should use the `snake_case` style, *not* `CamelCase` or
35 | `StUdLyCaPs` style.
36 |
37 | Boolean fields
38 | --------------
39 |
40 | Boolean fields should be named so that the name completes the phrase "This is
41 | _____" or "This has ____". For example, if you need a field to indicate whether
42 | the item in question is enabled, then "enabled" would be the proper form, as
43 | opposed to something like "is_enabled". Similarly, to indicate a network that
44 | uses DHCP, the field name "dhcp_enabled" should be used, rather than forms such
45 | as "enable_dhcp" or just "dhcp".
46 |
47 | It is also strongly recommended that negative naming be avoided, so use
48 | 'enabled' instead of 'disabled' or 'not_enabled'. The reason for this is that
49 | it is difficult to understand double negatives when reading code. In this case,
50 | "not_enabled = False" is harder to understand than "enabled = True".
51 |
52 | Boolean parameters
53 | ------------------
54 |
55 | There are two types of boolean parameters: those that are used to supply the
56 | value for a boolean field as described above, and those that are used to
57 | influence the behavior of the called method. In the first case, the name of the
58 | parameter should match the name of the field. For example, if you are supplying
59 | data to populate a field named 'enabled', the parameter name should also be
60 | 'enabled'. In the second case, though, where the parameter is used to toggle
61 | the behavior of the called method, the name should be more verb-like. A example
62 | of this form is the parameter "force", which is commonly used to indicate that
63 | the method should carry out its action without the normal safety checks. And as
64 | with boolean fields, the use of negative naming for boolean parameters is
65 | strongly discouraged, for the same reasons.
66 |
67 | State vs. Status
68 | ----------------
69 |
70 | While these two names mean nearly the same thing, there are differences. In
71 | general, 'state' should be used when recording where in a series of steps a
72 | process is in. In other words, 'state' is expected to change, and then only to
73 | a small number of subsequent states. An example of this would be the building
74 | of a VM, which follows a series of steps, and either moves forward to the next
75 | state, or falls into an ERROR state.
76 |
77 | Status, on the other hand, should be used for cases where there is no
78 | expectation of a series of changes. A service may have the status of "up" or
79 | "active", and it is expected that it should remain like that unless either and
80 | admin changes it, or a failure occurs.
81 |
--------------------------------------------------------------------------------
/guidelines/pagination_filter_sort.rst:
--------------------------------------------------------------------------------
1 | Pagination, Filtering, and Sorting
2 | ==================================
3 |
4 | This topic document serves to provide guidance on how to handle the
5 | pagination of large result sets and the best ways to provide filtering
6 | and sorting capabilities in a project's public REST API.
7 |
8 | Pagination
9 | ----------
10 |
11 | Pagination can be implemented using one or both of two query parameters:
12 |
13 | - ``limit`` to define the number of items returned in the response, and
14 |
15 | - ``marker`` to specify the ID of the last seen item
16 |
17 | Note that the marker need not always be the ID/UUID field of the record; it can
18 | be any field or combination of fields that will uniquely identify that record.
19 | Using a marker that is not unique will result in overlap or skipped records
20 | between paged results.
21 |
22 | For example::
23 |
24 | GET /app/items?limit=30
25 |
26 | Would return *at most* 30 items::
27 |
28 | {
29 | "items": [
30 | {
31 | "id": "719aae5f70db4364850f6198ea874aa6",
32 | "foo": "bar",
33 | "baz": "quux",
34 | "size": 9
35 | },
36 | ...
37 | {
38 | "id": "08ec231f6d9a43dda97d4b950c3393df",
39 | "foo": "buzz",
40 | "baz": "honk",
41 | "size": 6
42 | }
43 | ]
44 | }
45 |
46 | If, we then wanted to request the next 30 items after the last one, we would
47 | do::
48 |
49 | GET /app/items?limit=30&marker=08ec231f6d9a43dda97d4b950c3393df
50 |
51 | This would return the next (at most) 30 items after the item with ID
52 | ``08ec231f6d9a43dda97d4b950c3393df``.
53 |
54 | The ability to page through results implies that the items are sorted in a
55 | consistent fashion in each request, even if that order is nothing more than the
56 | order the items were added to the dataset. If the order of the results changes
57 | between requests, the returned pages will not have a meaningful relation to
58 | each other.
59 |
60 | A similar consideration would be how to handle the situation when the item
61 | whose ID is the ``marker`` value is deleted in between requests. In that event,
62 | the response should start with the next item logically. The definition of
63 | "logical" is necessarily fuzzy, and will depend on how the data is sorted.
64 | There may be some cases, however, where it is not reasonable to try to
65 | determine what the next logical item would be. In those cases, a **400 Bad
66 | Request** response should be returned, with a clear explanation in the error
67 | message that the requested marker value does not exist.
68 |
69 | Pagination Links
70 | ~~~~~~~~~~~~~~~~
71 |
72 | It is also helpful to users if services generate pagination links and include
73 | them in the :ref:`links` portion of the response body. Providing the following
74 | link types will make pagination navigation easier:
75 |
76 | - first
77 | - prev
78 | - self
79 | - next
80 | - last
81 |
82 | It is important to note that unless the data being paged is static, these links
83 | cannot be guaranteed to be accurate. For example, if some items are deleted,
84 | the ``prev`` link might contain some items from the current result.
85 |
86 | For example, a response to::
87 |
88 | GET /app/items?limit=30&marker=752b0b9997f24be49e5a1d89d1c53279
89 |
90 | Would look more akin to::
91 |
92 |
93 | {
94 | "items": [
95 | {
96 | "id": "719aae5f70db4364850f6198ea874aa6",
97 | "foo": "bar",
98 | "baz": "quux",
99 | "size": 9
100 | },
101 | ...
102 | {
103 | "id": "08ec231f6d9a43dda97d4b950c3393df",
104 | "foo": "buzz",
105 | "baz": "honk",
106 | "size": 6
107 | }
108 | ],
109 | "links": [
110 | {
111 | "rel": "self",
112 | "href": "http://example.com/app/items?limit=30&marker=752b0b9997f24be49e5a1d89d1c53279",
113 | },
114 | {
115 | "rel": "first",
116 | "href": "http://example.com/app/items?limit=30",
117 | },
118 | {
119 | "rel": "prev",
120 | "href": "http://example.com/app/items?limit=30&marker=eff79f5b4f8743caa1f775846302c1d5",
121 | },
122 | {
123 | "rel": "next",
124 | "href": "http://example.com/app/items?limit=30&marker=08ec231f6d9a43dda97d4b950c3393df",
125 | },
126 | {
127 | "rel": "last",
128 | "href": "http://example.com/app/items?limit=30&marker=6835afb7ea29491bb2722c6c43f1f070",
129 | }
130 | ]
131 | }
132 |
133 | When using links, the links that are included change based on which page the
134 | user requested. For example, if the user has requested the first page, then it
135 | still makes sense to include ``first``, ``self``, ``next``, and ``last`` but
136 | not ``prev``. Likewise if it is the last page, then including ``next`` is
137 | optional but the rest (``first``, ``prev``, ``self``, ``last``) is sensible.
138 |
139 | It should also be emphasized that calculating the ``last`` link can be costly.
140 | In many cases, such link calculation would require querying the entire dataset.
141 | Therefore implementing the ``last`` link is optional.
142 |
143 | Link Header Alternative
144 | ~~~~~~~~~~~~~~~~~~~~~~~
145 |
146 | If services are not including JSON `Hyper-Schema`_ links in their responses,
147 | or if they cannot include them for some reasons, they should return pagination
148 | links in the ``Link`` header as defined in :rfc:`5988` and :rfc:`6903`.
149 |
150 | .. note::
151 |
152 | Adding the ``Link`` to responses should not be considered an API contract
153 | change that needs a either a minor version bump or a microversion. Because
154 | of the nature of HTTP headers and the relationship of REST services with
155 | proxies, load balancers and API gateways, HTTP clients must already handle
156 | the existence of additional headers that may not be relevant.
157 |
158 | Consuming pagination is a fundamental operation that is frequently not done
159 | on a per-service basis. Requiring a user to undergo a microversion
160 | negotiation or minor version is extra per-service work that is both difficult
161 | and which carries no value. Users can simply check to see if a Link header
162 | exists, and if one does, they can consume the data in it.
163 |
164 | Filtering
165 | ---------
166 |
167 | Filtering can be implemented as a query parameter named for the field to be
168 | filtered on, the value should (naturally) be the value you need to filter for.
169 |
170 | An existing example of filtering in
171 | `Nova `_
172 | It is notable that Nova doesn't support OR filters, requiring
173 | separate requests per query.
174 |
175 | A different strategy is to specify query objects and pass them as a single
176 | URL-encoded JSON list. This is less client-friendly because it requires extra
177 | encoding steps.
178 |
179 | The simplest way to allow filtering is to map filterable parameters to query
180 | parameters.
181 | Take the sample object::
182 |
183 | GET /app/items
184 | {
185 | "items": [
186 | {
187 | "foo": "bar",
188 | "baz": "quux",
189 | "size": 9
190 | },
191 | {
192 | "foo": "buzz",
193 | "baz": "honk",
194 | "size": 6
195 | }
196 | ]
197 | }
198 |
199 | To filter on a field, simply add that field and its value to the query.::
200 |
201 | GET /app/items?foo=buzz
202 | {
203 | "items": [
204 | {
205 | "foo": "buzz",
206 | "baz": "honk",
207 | "size": 9
208 | }
209 | ]
210 | }
211 |
212 | Multiple filters result in an implicit AND, so in our example
213 | ``/app/items?foo=buzz&baz=quux`` would provide no results.
214 |
215 | **IN** operations are available for single fields, using comma-separated
216 | options for the field value and colon separation for the ``in``
217 | operator. The value must be in the list of values provided for the query
218 | to succeed.::
219 |
220 | GET /app/items?foo=in:buzz,bar
221 | {
222 | "items": [
223 | {
224 | "foo": "bar",
225 | "baz": "quux",
226 | "size": 9
227 | },
228 | {
229 | "foo": "buzz",
230 | "baz": "honk",
231 | "size": 6
232 | }
233 | ]
234 | }
235 |
236 | If values contain commas, they can be quoted similar to CSV escaping. For
237 | example, a query for the value ``a,bc`` or ``d`` would be
238 | ``?foo=in:"a,bc",d``. If values contain double-quotes, those can be
239 | backslashed inside quotes. Newline ("\n") and carriage return ("\r") escapes
240 | are also allowed. Actual backslashes must be doubled. For a value ``a"b\c``
241 | the query would be ``?foo="a\"b\\c"``. Unquoted values may not contain quotes
242 | and backslashes are treated as any other character. So for a value ``a\b``
243 | the query would be ``?foo=a\b``.
244 |
245 | For queries that need comparisons other than simple equals, operators are
246 | supported for membership, non-membership, inequality, greater-than,
247 | greater-than-or-equal, less-than, and less-than-or-equal-to. In order, the
248 | operators are: ``in``, ``nin``, ``neq``, ``gt``, ``gte``, ``lt``, and ``lte``.
249 | Simple equality is the default operation, and is performed as ``?param=foo``.
250 |
251 | They can be used in queries compounded with the values they work on. For
252 | example, finding objects with a size greater than 8 would be written as
253 | ``?size=gt:8`` and would return::
254 |
255 | GET /app/items?size=gt:8
256 | {
257 | "items": [
258 | {
259 | "foo": "bar",
260 | "baz": "quux",
261 | "size": 9
262 | }
263 | ]
264 | }
265 |
266 | Operators must be followed by colons, so the query ``?foo=gte`` searches for
267 | the literal string "gte" and searching for "gte:" can be done by quoting the
268 | value as ``?foo="gte:"``.
269 |
270 | **TODO:** Add guidance on a "LIKE" or regex operator to search text.
271 |
272 | Paginating responses should be done *after* applying the filters in a query,
273 | because it's possible for there to be no matches in the first page of results,
274 | and returning an empty page is a poor API when the user explicitly requested a
275 | number of results.
276 |
277 | Time based filtering queries
278 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
279 |
280 | To support filtering based on time intervals such as mentioned in the `ISO8601
281 | intervals wikipedia page`_, it should be possible to express the following
282 | use cases through API queries:
283 |
284 | * a two-ISO8601-date timestamp interval
285 | * an open-ended, single-ISO8601-date interval
286 | * multiple time intervals an item may belong to
287 | * equality with a default value where no time has been set yet
288 |
289 | .. _ISO8601 intervals wikipedia page: https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
290 |
291 | For instance, the `Ironic Inspector`_ project keeps track of node introspection
292 | statuses that include the ``started_at`` and ``finished_at`` fields. While the
293 | former value is always present, the latter is present only if the introspection
294 | finished::
295 |
296 | GET /app/item
297 | {
298 | "items": [
299 | {"id": "item1", "started_at": "2016-10-10T15:00Z",
300 | "finished_at": "2016-10-10T15:30Z"},
301 | {"id": "item2", "started_at": "2016-10-10T15:15Z",
302 | "finished_at": "2016-10-10T16:00Z"},
303 | {"id": "item3", "started_at": "2016-10-10T15:45Z",
304 | "finished_at": null}
305 | ]
306 | }
307 |
308 | .. _Ironic Inspector: https://docs.openstack.org/ironic-inspector/latest/
309 |
310 | To obtain items that finished between 15:30 and 16:00 UTC Today use an
311 | interval with two boundaries::
312 |
313 | GET /app/items?finished_at=ge:15:30&finished_at=lt:16:00
314 | {
315 | "items": [
316 | {"id": "item1", "started_at": "2016-10-10T15:00Z",
317 | "finished_at": "2016-10-10T15:30Z"}
318 | ]
319 | }
320 |
321 | To list items that finished any time after 15:30 UTC Today, use an
322 | open-ended time interval query::
323 |
324 | GET /app/items?finished_at=ge:15:30
325 | {
326 | "items": [
327 | {"id": "item1", "started_at": "2016-10-10T15:00Z",
328 | "finished_at": "2016-10-10T15:30Z"},
329 | {"id": "item2", "started_at": "2016-10-10T15:15Z",
330 | "finished_at": "2016-10-10T16:00Z"}
331 | ]
332 | }
333 |
334 | Finally, to include items that didn't finish yet, use the default value
335 | equality. Since the queries are implicitly AND-ed, use two requests::
336 |
337 | GET /app/items?finished_at=ge:16:00
338 | {
339 | "items": [
340 | {"id": "item2", "started_at": "2016-10-10T15:15Z",
341 | "finished_at": "2016-10-10T16:00Z"}
342 | ]
343 | }
344 | GET /app/items?finished_at=null
345 | {
346 | "items": [
347 | {"id": "item3", "started_at": "2016-10-10T15:45Z",
348 | "finished_at": null}
349 | ]
350 | }
351 |
352 |
353 | Sorting
354 | -------
355 |
356 | Sorting is determined through the use of the 'sort' query string parameter. The
357 | value of this parameter is a comma-separated list of sort keys. Sort directions
358 | can optionally be appended to each sort key, separated by the ':' character.
359 |
360 | The supported sort directions are either 'asc' for ascending or 'desc' for
361 | descending.
362 |
363 | The caller may (but is not required to) specify a sort direction for each key.
364 | If a sort direction is not specified for a key, then a default is set by the
365 | server.
366 |
367 | For example:
368 |
369 | - Only sort keys specified:
370 |
371 | + ``sort=key1,key2,key3``
372 | + 'key1' is the first key, 'key2' is the second key, etc.
373 | + Sort directions are defaulted by the server
374 |
375 | - Some sort directions specified:
376 |
377 | + ``sort=key1:asc,key2,key3``
378 | + Any sort key without a corresponding direction is defaulted
379 | + 'key1' is the first key (ascending order), 'key2' is the second key
380 | (direction defaulted by the server), etc.
381 |
382 | - Equal number of sort keys and directions specified:
383 |
384 | + ``sort=key1:asc,key2:desc,key3:asc``
385 | + Each key is paired with the corresponding direction
386 | + 'key1' is the first key (ascending order), 'key2' is the second key
387 | (descending order), etc.
388 |
389 | Note that many projects have implemented sorting using repeating 'sort_key'
390 | and 'sort_dir' query string parameters, see [1]. As these projects adopt these
391 | guidelines, they should deprecate the older parameters appropriately.
392 |
393 | [1]: https://wiki.openstack.org/wiki/API_Working_Group/Current_Design/Sorting
394 |
395 |
396 | .. _Hyper-Schema:
397 | http://json-schema.org/latest/json-schema-hypermedia.html
398 |
--------------------------------------------------------------------------------
/guidelines/representation_structure.rst:
--------------------------------------------------------------------------------
1 | Representation Structure Conventions
2 | ====================================
3 |
4 | Singular resources
5 | ------------------
6 |
7 | TODO
8 |
9 | Collection resources
10 | --------------------
11 |
12 | JSON request and response representations for collection resources should be
13 | an object that includes a top-level property to encapsulate the collection of
14 | resources. The value of this property should be a JSON array whose elements are
15 | the JSON representations of the resources in the collection.
16 |
17 | For example, when listing networks using the ``GET /networks`` API,
18 |
19 | - The JSON response representation would be structured as follows:
20 |
21 | .. code-block:: javascript
22 |
23 | {
24 | "networks": [
25 | {
26 | // Properties of network #1
27 | },
28 | {
29 | // Properties of network #2
30 | }
31 | ]
32 | }
33 |
34 | *Rationale*: Having JSON collection resource representations be an object
35 | — as opposed to an array — allows the representation to be extensible. For
36 | instance, properties that represent collection-level metadata could be
37 | added at a later time.
38 |
39 | Here are some other OpenStack APIs that use this structure:
40 |
41 | - `Bulk creating networks `_,
42 | which uses the top-level ``networks`` property in the JSON request and
43 | response representations.
44 | - `Listing stacks `_,
45 | which uses the top-level ``stacks`` property in the JSON response
46 | representation.
47 | - `Listing database instances `_,
48 | which uses the top-level ``instances`` property in the JSON response
49 | representation.
50 | - `Listing servers `_,
51 | which uses the top-level ``servers`` property in the JSON response
52 | representation.
53 |
--------------------------------------------------------------------------------
/guidelines/sdk-exposing-microversions.rst:
--------------------------------------------------------------------------------
1 | Exposing microversions in SDKs
2 | ==============================
3 |
4 | While we are striving to design OpenStack API as easy to use as possible, SDKs
5 | for various programming languages will always be an important part of
6 | experience for developers, consuming it. This documentation contains
7 | recommendations on how to deal with :doc:`microversions
8 | ` in SDKs (software development kits)
9 | targeting OpenStack.
10 |
11 | This document recognizes two types of deliverables that we usually call SDKs.
12 | They will differ in the recommended approaches to exposing microversions
13 | to their consumers.
14 |
15 | * `High-level SDK`_ or just `SDK` is one that hides details of the underlying
16 | API from consumers, building its own abstraction layers. Its approach
17 | to backward and forward compatibility, as well as feature discovery, is
18 | independent of the one used by the underlying API. Shade_ is an example of
19 | such SDK for OpenStack.
20 |
21 | * `Language binding`_ closely follows the structure and design of the
22 | underlying API. It usually tries to build as little additional
23 | abstraction layers on top of the underlying API as possible. Examples
24 | include all OpenStack ``python-client`` libraries.
25 |
26 | .. note::
27 | If in doubt, you should write a high-level SDK. The benefit of using an
28 | SDK is in consuming API in a way, natural to the programming language and
29 | any used frameworks. Things like microversions are likely to look foreign
30 | and confusing for developers who do not specialize on API design.
31 |
32 | Concepts used in this document:
33 |
34 | consumer
35 | programming code that interfaces with an SDK, as well as its author.
36 | microversion
37 | API version as defined in :doc:microversion_specification. For simplicity,
38 | this guideline uses `version` as a synonym of `microversion`.
39 |
40 | .. note::
41 | When using the word ``microversion`` in your SDK, be careful to avoid
42 | associations with semantic versioning. A microversion is not the same
43 | as a patch version, and can be even major in a sense of semantic
44 | versioning.
45 | major version
46 | is not really an API version in a sense of :doc:microversion_specification,
47 | but rather a separate generation of the API, co-existing with other
48 | generations in the same HTTP endpoints tree.
49 |
50 | Major versions are distinguished in the URLs by ``/v`` parts and
51 | are the first components of a microversion. For example, in microversion
52 | ``1.42``, ``1`` is a major version.
53 |
54 | .. note::
55 | We don't seem to have an established name for the second component.
56 |
57 | As major versions may change the structure of API substantially, including
58 | changing the very mechanism of the microversioning, an SDK should generally
59 | try to stay within the requested major version, if any.
60 | negotiation
61 | process of agreeing on the most suitable common version between the client
62 | and the server. Negotiation should happen once, and its results should be
63 | cached for the whole session.
64 |
65 | .. note::
66 | We will use the Python programming language in all examples, but
67 | the recommendations will apply to any programming languages, including
68 | statically compiled ones. For examples here we will use
69 | a fictional Cats-as-a-Service API and its ``python-catsclient`` SDK.
70 |
71 | .. _Shade: https://docs.openstack.org/shade/latest/
72 |
73 | High-level SDK
74 | --------------
75 |
76 | Generally, SDKs should not expose underlying API microversions to users.
77 | The structure of input and output data should not depend on the microversion
78 | used. Means, specific to the programming language and/or data formats in use,
79 | should be employed to indicate absence or presence of certain features
80 | and behaviors.
81 |
82 | For example, a field, missing in the current microversion, can be
83 | expressed by ``None`` value in Python, ``null`` value in Java or its type
84 | can be ``Option`` in Rust:
85 |
86 | .. code-block:: python
87 |
88 | import catsclient
89 |
90 | sdk = catsclient.SDK()
91 |
92 | cat = sdk.get_cat('fluffy')
93 | if cat.color is None:
94 | print("Cat colors are not supported by this cat server")
95 | else:
96 | print("The cat is", cat.color)
97 |
98 | In this example, the SDK negotiates the API microversion that can return
99 | as much information as possible during the ``get_cat`` call. If the
100 | resulting version does not contain the ``color`` field, it is set to
101 | ``None``.
102 |
103 | An SDK should negotiate the highest microversion that will allow it to serve
104 | consumer's needs better. However, it should never negotiate a microversion
105 | outside of the range it was written and tested with to avoid confusing
106 | breakages on future changes to the API. It goes without saying that an SDK
107 | should not crush or exhibit undefined behavior on any microversion returned
108 | by a server. Any incompatibilities should be expressed as soon as possible
109 | in a form that is natural for the given programming language.
110 |
111 | For example, a Python SDK should raise an exception when a method is
112 | called that is not possible to express in any microversion supported by
113 | both the SDK and the server:
114 |
115 | .. code-block:: python
116 |
117 | import catsclient
118 |
119 | sdk = catsclient.SDK()
120 |
121 | cat = sdk.get_cat('fluffy')
122 | try:
123 | cat.bark()
124 | except catsclient.UnsupportedFeature:
125 | cat.meow()
126 |
127 | It is also useful to allow detecting supported features before
128 | using them:
129 |
130 | .. code-block:: python
131 |
132 | import catsclient
133 |
134 | sdk = catsclient.SDK()
135 |
136 | cat = sdk.get_cat('fluffy')
137 | if cat.can_bark():
138 | cat.bark()
139 | else:
140 | cat.meow()
141 |
142 | In this example, ``can_bark`` uses the negotiated microversion to check if
143 | it is possible for the ``bark`` call to work.
144 |
145 | .. note::
146 | If possible, an SDK should inform the consumer of the required API
147 | microversion and why it is not possible to use it. This is probably the
148 | only place where microversions can and should leak to a consumer.
149 |
150 | If possible, major versions should be treated the same way, and should not be
151 | exposed to users. If not possible, an SDK should pick the most recent
152 | major version from the available.
153 |
154 | Language binding
155 | ----------------
156 |
157 | A low-level SDKs, which is essentially just a language binding for the API,
158 | stays close to the underlying API. Thus, it must expose microversions
159 | to consumers, and must do it in a way, closest to how API does it. We
160 | recommend that all calls accept an explicit API microversion that is sent
161 | directly to the underlying API. If none is provided, no version should be sent:
162 |
163 | .. code-block:: python
164 |
165 | import catsclient
166 |
167 | client = catsclient.v1.get_client()
168 |
169 | cat = client.get_cat('fluffy') # executed with no explicit version
170 | try:
171 | cat.bark(api_version='1.42') # executed with 1.42
172 | except catsclient.IncompatibleApiVersion:
173 | # no support for 1.42, falling back to older behavior
174 | cat.meow() # executed with no explicit version
175 |
176 | .. note::
177 | In some programming languages, particularly those without default arguments
178 | for functions, it may be inconvenient to add a version argument to all
179 | calls. Other means may be used to achieve the same result, for example,
180 | temporary context objects:
181 |
182 | .. code-block:: python
183 |
184 | import catsclient
185 |
186 | client = catsclient.v1.get_client()
187 |
188 | cat = client.get_cat('fluffy') # executed with no explicit version
189 | with cat.use_api_version('1.42') as new_cat:
190 | new_cat.bark() # executed with 1.42
191 |
192 | Major versions
193 | ~~~~~~~~~~~~~~
194 |
195 | A low-level SDK should make it explicit which major version it is working
196 | with. It can be done by namespacing the API or by accepting an explicit
197 | major version as an argument. The preferred approach depends on how
198 | different the major versions of an API are.
199 |
200 | Using Python as an example, either
201 |
202 | .. code-block:: python
203 |
204 | import catsclient
205 | client = catsclient.v1.get_client()
206 |
207 | or
208 |
209 | .. code-block:: python
210 |
211 | import catsclient
212 | client = catsclient.get_client(1)
213 |
214 | Supported versions
215 | ~~~~~~~~~~~~~~~~~~
216 |
217 | It's highly recommended to provide a way to query the server for the
218 | supported version range:
219 |
220 | .. code-block:: python
221 |
222 | import catsclient
223 |
224 | client = catsclient.v1.get_client()
225 | min_version, max_version = client.supported_api_versions()
226 |
227 | cat = client.get_cat('fluffy') # executed with no explicit version
228 | if max_version >= (1, 42):
229 | cat.bark(api_version='1.42') # executed with 1.42
230 | else:
231 | # no support for 1.42, falling back to older behavior
232 | cat.meow() # executed with no explicit version
233 |
234 | Minimum version
235 | ~~~~~~~~~~~~~~~
236 |
237 | Applications often have a base minimum API version they are capable of working
238 | with. It is recommended to provide a way to accept such version and use it
239 | as a default when no explicit version is provided:
240 |
241 | .. code-block:: python
242 |
243 | import catsclient
244 |
245 | try:
246 | client = catsclient.v1.get_client(api_version='1.2')
247 | except catsclient.IncompatibleApiVersion:
248 | sys.exit("Cat API version 1.2 is not supported")
249 |
250 | cat = client.get_cat('fluffy') # executed with version 1.2
251 | try:
252 | cat.bark(api_version='1.42') # executed with 1.42
253 | except catsclient.IncompatibleApiVersion:
254 | # no support for 1.42, falling back to older behavior
255 | cat.meow() # executed with version 1.2
256 |
257 | As in this example, an SDK using this approach must provide a clear way to
258 | indicate that the requested version is not supported and do it as early as
259 | possible.
260 |
261 | List of versions
262 | ~~~~~~~~~~~~~~~~
263 |
264 | As a simplification extension, a language binding may accept a list of versions
265 | as a base version. The highest version supported by the server must be picked
266 | and used as a default.
267 |
268 | .. code-block:: python
269 |
270 | import catsclient
271 |
272 | try:
273 | client = catsclient.v1.get_client(api_version=['1.0', '1.42'])
274 | except catsclient.IncompatibleApiVersion:
275 | sys.exit("Neither Cat API 1.0 nor 1.42 is supported")
276 |
277 | cat = client.get_cat('fluffy') # executed with either 1.0 or 1.42
278 | # whichever is available
279 | if client.current_api_version == (1, 42):
280 | # Here we know that the negotiated version is 1.42
281 | cat.bark() # executes with 1.42
282 | else:
283 | # Here we know that the negotiated version is 1.0
284 | cat.meow() # executes with 1.0
285 |
286 | # The default version can still be overwritten
287 | try:
288 | cat.drink(catsclient.MILK, api_version='1.66') # executed with 1.66
289 | except catsclient.IncompatibleApiVersion:
290 | # no support for 1.66, falling back to older behavior
291 | cat.drink() # executed with either 1.0 or 1.42 whichever is available
292 |
--------------------------------------------------------------------------------
/guidelines/tags.rst:
--------------------------------------------------------------------------------
1 | .. _tags:
2 |
3 | Tags
4 | ====
5 |
6 | This topic document serves to provide guidance on how to work with tags in
7 | OpenStack REST APIs.
8 |
9 | Tags are often confused with metadata. While the two have an intersection, the
10 | main function of tags is to classify a collection of entities in groups, while
11 | metadata is used to attach additional information to entities. A separate
12 | guideline document exists for metadata.
13 |
14 | For background on the REST guidelines referenced here, see the topic documents
15 | on :ref:`naming` and :ref:`http`.
16 |
17 | Tags Representation
18 | -------------------
19 |
20 | Tags are strings attached to an entity with the purpose of classification into
21 | groups.
22 |
23 | An entity can have zero, one or more tags associated with it, for that
24 | reason the recommended representation within the parent entity is a list.
25 |
26 | Example request using a server resource::
27 |
28 | GET /servers/1234567890
29 |
30 | Response::
31 |
32 | {
33 | 'id': '1234567890',
34 |
35 | ... other server resource properties ...
36 |
37 | 'tags': ['foo', 'bar', 'baz']
38 | }
39 |
40 | Updates to the tags are issued in accordance to the standard HTTP request
41 | methods, issued directly against the parent resource. To update the tag list
42 | of a resource, a PUT request should be sent to the resource, including not only
43 | the updated tag list, but the complete resource representation in the body. The
44 | update in this case does not need to be limited to tags, other properties can
45 | be updated at the same time. Note that by using a PUT request it is possible to
46 | add and/or remove multiple tags in one single operation, simply by sending
47 | the updated tag list with the resource representation.
48 |
49 | For resources that have a representation that is not in JSON a separate
50 | endpoint must be created to expose the tags. See the "Tag Resource URLs"
51 | section below for more information.
52 |
53 | Tags Restrictions
54 | -----------------
55 |
56 | Tags are strings with the following basic restrictions:
57 |
58 | * Tags are case sensitive.
59 | * '/' is **not** allowed to be in a tag name
60 | * Comma is **not** allowed to be in a tag name in order to simplify requests
61 | that specify lists of tags
62 | * All other characters are allowed to be in a tag name
63 |
64 | .. note::
65 |
66 | The '/' character is forbidden because some servers have a problem with
67 | encoding this character. The problem is that the server will handle '%2F'
68 | as '/' even though '/' is encoded. It's a problem of poor server
69 | implementation. To avoid problems with handling URLs character '/' is
70 | forbidden in tag names.
71 |
72 | Character Encoding for Tags
73 | ---------------------------
74 |
75 | Per :rfc:`7159#section-8.1`, JSON documents shall be encoded in UTF-8, UTF-16,
76 | or UTF-32, with UTF-8 being the default and the recommended encoding for
77 | maximum interoperability.
78 |
79 | Since the tags are part of a JSON document, the encoding of the tag names must
80 | match the encoding of the parent document. The use of UTF-8 encoding is
81 | strongly recommended.
82 |
83 | Tags Resource URLs
84 | ------------------
85 |
86 | Sometimes it may be inconvenient to work with the tags portion of a resource
87 | using the complete resource representation, so tags can optionally be exposed
88 | as a stand-alone resource as well. If a project decides to provide this
89 | functionality, then the root resource URL for tag management should be
90 | the URL of the resource to which the tags belong, followed by */tags* (for
91 | APIs that use user-generated URLs with varying number of components the *tags/*
92 | URL component can be added as a prefix instead of a suffix).
93 |
94 | For example, the resource identified by URL
95 | *http://example.com:8774/servers/1234567890* must expose its tags with
96 | root URL *http://example.com:8774/servers/1234567890/tags*.
97 |
98 | Obtaining the Tag List
99 | ~~~~~~~~~~~~~~~~~~~~~~
100 |
101 | To obtain the tags for a resource, a GET request must be sent to the root
102 | tags URL. On success, the server responds with a 200 status code and the
103 | complete set of tags items in the response body.
104 |
105 | Example request::
106 |
107 | GET /servers/1234567890/tags
108 |
109 | Response::
110 |
111 | {
112 | "tags": ['foo', 'bar', 'baz']
113 | }
114 |
115 | Note that this representation differs from the one adopted by Nova. One reason
116 | is that with this structure it is possible to add additional metadata to the
117 | request body. A secondary reason is that JSON arrays as a top level entity
118 | have been found to expose vulnerabilities in browsers, as reported by the
119 | following articles:
120 |
121 | - http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/
122 | - http://haacked.com/archive/2009/06/25/json-hijacking.aspx/
123 |
124 | Modifying the Tag List
125 | ~~~~~~~~~~~~~~~~~~~~~~
126 |
127 | To add, remove, or change tags, a PUT request should be sent to the
128 | root tags URL, with the updated complete list of tags in the body of the
129 | request. On success, the server responds with a 200 status code and the
130 | complete updated tag list in the response body.
131 |
132 | Example request (removes "bar" and adds "qux")::
133 |
134 | PUT /servers/1234567890/tags
135 | {
136 | "tags": ['foo', 'baz', 'qux']
137 | }
138 |
139 | Response::
140 |
141 | {
142 | "tags": ['foo', 'baz', 'qux']
143 | }
144 |
145 | If the number of tags exceeds the limit allowed by the API, the return code
146 | should be **400 Bad Request** as the HTTP Guidelines describe. To achieve
147 | request success, the client should change the requested number of tags to
148 | be less than the API limit.
149 |
150 | Deleting Tags
151 | ~~~~~~~~~~~~~
152 |
153 | To delete the entire tag list associated with a resource, a DELETE
154 | request must be sent to the root tags URL. On success, the server responds
155 | with a 204 status code.
156 |
157 | Example request::
158 |
159 | DELETE /servers/1234567890/tags
160 |
161 | Addressing Individual Tags
162 | ~~~~~~~~~~~~~~~~~~~~~~~~~~
163 |
164 | To provide even more fine-grained access to tags, another optional extension is
165 | to expose resource URLs for individual tags. If a project decides to implement
166 | this option, then each tag should be accessed individually at a URL formed by
167 | appending the tag name to the root tag URL. Note that this option is not
168 | available for APIs that use user-generated URLs.
169 |
170 | To insert a single tag without having to send the entire tag list, the client
171 | should send a PUT request to the inidividual tag URL. On success, the server
172 | responds with a 201 status code and includes the new tag's URL in the
173 | ``Location`` header in the response.
174 |
175 | Example request::
176 |
177 | PUT /servers/1234567890/tags/qux
178 |
179 |
180 | Response::
181 |
182 | Location: http://example.com:8774/servers/1234567890/tags/qux
183 |
184 |
185 | To check if a tag exists or not, the client should send a HEAD request to the
186 | individual tag URL. If the tag exists, the server responds with a status code
187 | 204 and no response body. If the tag does not exist, the server responds with
188 | a status code 404.
189 |
190 | To delete a single tag without affecting the remaining ones, a
191 | DELETE request is sent to the individual tag URL. On success, the server
192 | responds with a 204 status code. If an invalid tag is given, a 404 response
193 | is returned.
194 |
195 | Example request::
196 |
197 | DELETE /servers/1234567890/tags/qux
198 |
199 | Filtering and Searching by Tags
200 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201 |
202 | To search the collection of entities by their tags, the client should send a
203 | GET request to the collection URL, and include query string parameters that
204 | define the query. These arguments can be combined with other arguments, such
205 | as those that perform additional filtering outside of tags, pagination,
206 | sorting, etc. The recommended query string arguments for filtering tags are
207 | ``tags``, ``tags-any``, ``not-tags`` and ``not-tags-any``.
208 |
209 | Note that once again this is different than the nova specification, which
210 | uses repeated ``tag`` query arguments to specify a list of tags. The preference
211 | here is to be consistent with the sorting guideline document, for which it
212 | was decided that repeating query string arguments is not a good idea due to
213 | not having good support among web clients and servers.
214 |
215 | To request the list of entities that have a single tag, ``tags`` argument
216 | should be set to the desired tag name. Example::
217 |
218 | GET /servers?tags=red
219 |
220 | To request the list of entities that have two or more tags, the ``tags``
221 | argument should be set to the list of tags, separated by commas. In this
222 | situation the tags given must all be present for an entity to be included in
223 | the query result. Example that returns servers that have the "red" and "blue"
224 | tags::
225 |
226 | GET /servers?tags=red,blue
227 |
228 | To request the list of entities that have one or more of a list of given tags,
229 | the ``tags-any`` argument should be set to the list of tags, separated by
230 | commas. In this situation as long as one of the given tags is present the
231 | entity will be included in the query result. Example that returns the servers
232 | that have the "red" or the "blue" tag::
233 |
234 | GET /servers?tags-any=red,blue
235 |
236 | To request the list of entities that do not have one or more tags, the
237 | ``not-tags`` argument should be set to the list of tags, separated by commas.
238 | In this situation only the entities that do not have any of the given tags will
239 | be included in the query results. Example that returns the servers that do not
240 | have the "red" nor the "blue" tag::
241 |
242 | GET /servers?not-tags=red,blue
243 |
244 | To request the list of entities that do not have at least one of a list of
245 | tags, the ``not-tags-any`` argument should be set to the list of tags,
246 | separated by commas. In this situation only the entities that do not have at
247 | least one of the given tags will be included in the query result. Example that
248 | returns the servers that do not have the "red" tag, or do not have the "blue"
249 | tag::
250 |
251 | GET /servers?not-tags-any=red,blue
252 |
253 | The ``tags``, ``tags-any``, ``not-tags`` and ``not-tags-any`` arguments can be
254 | combined to build more complex queries. Example::
255 |
256 | GET /servers?tags=red,blue&tags-any=green,orange
257 |
258 | The above example returns any servers that have the "red" and "blue" tags, plus
259 | at least one of "green" and "orange".
260 |
261 | It is possible to create a request which is self-contradictory. Example::
262 |
263 | GET /servers?tags=red¬-tags=red
264 |
265 | This should be treated as a valid request (ie *not* a client error), and should
266 | return an empty result-set with a 2xx status code.
267 |
--------------------------------------------------------------------------------
/guidelines/terms.rst:
--------------------------------------------------------------------------------
1 | Terms
2 | =====
3 |
4 | As Phil Karlson [once said](http://martinfowler.com/bliki/TwoHardThings.html):
5 |
6 | > There are only two hard things in Computer Science: cache invalidation and
7 | > naming things.
8 |
9 | Over time, various terms and synonyms for those terms generate some
10 | controversy, and different teams end up using different words to reference the
11 | same object or resource. This document serves to record decisions that were
12 | made regarding certain terms, and attempts to succinctly define each term.
13 |
14 | * **project** vs. **tenant**
15 |
16 | **project** shall be used to describe the concept of a group of OpenStack
17 | users that share a common set of quotas. The older term **tenant** should
18 | *not* be used in OpenStack REST APIs.
19 |
20 | * **server** vs. **instance**
21 |
22 | **server** shall be used to describe a virtual machine, a
23 | bare-metal machine, or a containerized virtual machine that is used
24 | by OpenStack users for compute purposes. The older term
25 | **instance** that is also by Amazon Web Services EC2 API to
26 | describe a virtual machine, should *not* be used in OpenStack REST
27 | APIs.
28 |
29 | * **project name** vs. **service type**
30 |
31 | Most OpenStack projects have both a "project name" (e.g., Nova, Keystone,
32 | etc.) and a "service type" (e.g., Compute, Identity, etc.). Some REST API
33 | features (e.g., JSON-Home, API Microversions, etc.) need to expose each
34 | project in a request/response.
35 | The project *should* be represented with its **service type** if it has both
36 | a "project name" and a "service type" because its project name is subject to
37 | change (e.g., Neutron was Quantum) and its service type is more stable. The
38 | service type should come from "type" of the corresponding OpenStack Identity
39 | service catalog entry.
40 |
--------------------------------------------------------------------------------
/guidelines/testing.rst:
--------------------------------------------------------------------------------
1 | Testing
2 | =======
3 |
4 | This topic document serves to provide guidance on how to consistently
5 | and effectively test a project's public HTTP API.
6 |
7 | Current State of Testing
8 | ------------------------
9 |
10 | **TODO** Enumerate the variety of HTTP API testing styles and systems
11 | used throughout OpenStack APIs.
12 |
13 | Goals
14 | -----
15 |
16 | There are many aspects to testing and at least as many stakeholders in
17 | their creation and use. Tests can operate at least three levels:
18 |
19 | 1. To validate or assist in the creation of new functionality or changes
20 | to existing code.
21 | 2. To prevent regressions.
22 | 3. To allow inspection and analysis of the system being tested.
23 |
24 | API tests should strive to enable each of these without limiting the
25 | others.
26 |
27 | Proposals
28 | ---------
29 |
30 | 1. Each project should have a suite of declarative tests which
31 | exercise the full breadth of the API, closely mirroring the HTTP
32 | requests and responses. Being declarative allows easy inspection
33 | for those who wish to create or understand clients of the service.
34 | It is also instructive in revealing a certain lack of grace in API
35 | construction which can be obscured by code-based tests.
36 |
37 | 2. Black-box testing of APIs is desirable. They do not strictly require
38 | a web service to be run. WSGI applications can be called directly
39 | with constructed environments, or using intercepts.
40 |
41 | 3. **your input here**
42 |
--------------------------------------------------------------------------------
/guidelines/time.rst:
--------------------------------------------------------------------------------
1 | Date and Time Conventions
2 | =========================
3 |
4 | This topic document serves to provide guidance on how to format dates and times
5 | in the OpenStack public REST APIs.
6 |
7 | REST API
8 | --------
9 |
10 | * APIs should use ISO 8601 format to return dates and times in resource
11 | representations. For more information see [1]_.
12 | * It is recommended that the Coordinated Universal Time (UTC) is used to avoid
13 | differences in time. For more information see [2]_.
14 | * Clients should also use the ISO 8601 format when providing dates to the
15 | server. The API server should be able to parse and interpret any valid
16 | ISO 8601 timestamp in any timezone.
17 |
18 | .. [1] http://en.wikipedia.org/wiki/ISO_8601
19 | .. [2] http://en.wikipedia.org/wiki/Coordinated_Universal_Time
20 |
--------------------------------------------------------------------------------
/guidelines/uri.rst:
--------------------------------------------------------------------------------
1 | ..
2 | This work is licensed under a Creative Commons Attribution 3.0 Unported
3 | License.
4 |
5 | http://creativecommons.org/licenses/by/3.0/legalcode
6 |
7 |
8 | ==============
9 | Effective URIs
10 | ==============
11 |
12 | Effective URIs (also sometimes referred to by the somewhat more specific term
13 | URL [#url]_) are central to the design of a usable HTTP API. Uniform
14 | Resource Identifiers are defined by :rfc:`3986` and have their use
15 | in HTTP clarified in :rfc:`7230#section-2.7`. A URI is the identifier
16 | of a resource in an API and is also used to locate and address that
17 | resource on network.
18 |
19 | A URI is divided up into several sections: `scheme`, `authority`, `path`,
20 | `query`, and `fragment` (please refer to :rfc:`3986` for more detail).
21 | As a developer of API services, `path` and `query` are most relevant;
22 | `fragment` is not usually sent to the service and there is little
23 | opportunity nor reason to exert control over `scheme` and
24 | `authority`.
25 |
26 | What follows will concern itself solely with the `path` and `query`.
27 |
28 | Things Worth Knowing
29 | --------------------
30 |
31 | * The value of `path` and `query` are case sensitive. That is if you
32 | have two URIs that look similar,
33 | ``http://example.com/foo/BAR?dEtail=1``
34 | and ``http://example.com/foo/bar?detail=1``, they are not
35 | equivalent. A server application can choose to normalize the URI
36 | but this not recommended as it does not correspond with common
37 | use nor :rfc:`7230#section-2.7.3`.
38 |
39 | * While a `path` often looks like a hierarchical path
40 | (``/collection/item/sub-resource/detail``), akin to the
41 | full filename of a file on disk, this is a matter of convenience
42 | and an artifact of making the URI consumable by humans. The truth
43 | of the matter is that the entirety of the URI identifies the
44 | resource, not just the last segment of the path.
45 |
46 | * The entire point of URI design [#exitclause]_ is to make the URIs
47 | consumable by humans in a meaningful fashion both for users of the
48 | API and future maintainers of the API.
49 |
50 | General Advice on URI Design
51 | ----------------------------
52 |
53 | .. note:: This is far from an exhaustive list. This is merely a
54 | starting point from which we can accumulate reasonable advice on
55 | how to form good URIs.
56 |
57 | * Since a single URI identifies a single resource it is also useful
58 | for two more things to be kept true when building services:
59 |
60 | * Any resource should only have one URI. Where possible do not provide
61 | multiple ways to reference the same thing. Use HTTP redirects to
62 | resolve indirect requests to the correct canonical URI and
63 | content negotiation to request different representations.
64 |
65 | * Any given resource, since it should only have one URI, should
66 | respond to all relevant HTTP methods at just that URI and not have
67 | secondary URIs for some methods. For example:
68 |
69 | Use::
70 |
71 | GET /resources/1322b203bdc64c13b6e72b04d43e8690
72 | DELETE /resources/1322b203bdc64c13b6e72b04d43e8690
73 |
74 | Never::
75 |
76 | GET /resources/1322b203bdc64c13b6e72b04d43e8690
77 | DELETE /resources/1322b203bdc64c13b6e72b04d43e8690/delete
78 |
79 | * It is often the case that an API will have URIs that
80 | :doc:`represent collections ` for resources
81 | and individual members of that collection in a hierarchy. For example
82 | [#non-normative]_::
83 |
84 | GET /birds
85 |
86 | {"birds": [
87 | {
88 | "name": "alpha",
89 | "type": "crow"
90 | },
91 | {
92 | "name": "beta",
93 | "type": "jackdaw"
94 | }]
95 | }
96 |
97 | GET /birds/alpha
98 |
99 | {
100 | "name": "alpha",
101 | "type": "crow"
102 | }
103 |
104 | This is a reasonable thing to do as it goes a long way to making
105 | the elements of an API comprehensible.
106 |
107 | * If the hierarchy described above is used it is important that
108 | all URIs that take the second form (``/birds/alpha``) have the
109 | same semantics and are always identifying a resource which is a
110 | member of this collection (in this case "is a bird").
111 |
112 | There are multiple examples throughout OpenStack of this concept
113 | being violated. For example in the `os-cells` API in nova it is
114 | possible to ``GET /os-cells`` to get a list of cells, ``GET
115 | /os-cells/some-name`` to get information about a single cell named
116 | ``some-name`` and ``GET /os-cells/details`` to get the same
117 | information as ``GET /os-cells`` but with additional detail.
118 |
119 | For this particular example one way (of several options) to achieve
120 | the same result while preserving URI semantics would have been to use a
121 | `query` to augment the existing collection URI to indicate the
122 | desire for more detail [#non-normative]_::
123 |
124 | GET /os-cells?details=true
125 |
126 | .. todo:: There is an as yet unresolved debate on the best way to indicate
127 | boolean query parameters. Any of ``details=true``, ``details=1`` or
128 | ``details`` could make sense here. The above should not be taken to
129 | indicate support for any `query` format. Rather it is merely to
130 | demonstrate cleaner semantics in the `path` portion of the URI.
131 |
132 | Complex Queries
133 | ---------------
134 |
135 | In some cases it may be necessary to return a set of resources that match a set
136 | of filter conditions. For those situations, use the **GET** method, and create
137 | a query string that concatenates all the requirements. As an example, if you
138 | needed to return all the birds which are blue and are migratory and that swim,
139 | the URI would look like::
140 |
141 | GET /birds?color=blue&migratory=true&swimming=true
142 |
143 | There are restrictions on the length of URIs that vary depending on the server
144 | and client in use. The most restrictive are some browsers that have a maximum
145 | URI length of about 2K, while web servers such as Apache limit URIs to around
146 | 8K. If the length of the URI needed to express the complex requirements of a
147 | request may possibly exceed those limits, it is acceptable to use the ``POST``
148 | method with the filter conditions passed in the body of the request.
149 |
150 | .. rubric:: Footnotes
151 |
152 | .. [#url] https://en.wikipedia.org/wiki/Uniform_Resource_Locator
153 | .. [#exitclause] There is another school of thought which insists
154 | that URIs should be entirely opaque identifiers which computers
155 | use to exchange information. There's a lot of value in this
156 | line of thinking as it allows the identifiers to act as
157 | references to fungible referents, but it discounts the value and cost of
158 | creating a diverse collection of clients for services. If we
159 | wish to encourage that diverse collection then having URIs which
160 | are consumable by humans is helpful.
161 | .. [#non-normative] These are example requests and responses only and
162 | should not be taken as explicitly describing correct form.
163 |
--------------------------------------------------------------------------------
/guidelines/version-discovery-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "id": "https://specs.openstack.org/openstack/api-wg/_downloads/unversioned-discovery-schema.json#",
4 | "type": "object",
5 | "required": ["versions"],
6 | "additionalProperties": false,
7 | "properties": {
8 | "versions": {
9 | "type": "array",
10 | "items": {
11 | "$ref": "version-information-schema.json#"
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/guidelines/version-information-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "id": "https://specs.openstack.org/openstack/api-wg/_downloads/version-information-schema.json#",
4 | "type": "object",
5 | "additionalProperties":false,
6 | "required":[
7 | "status",
8 | "id",
9 | "links"
10 | ],
11 | "properties": {
12 | "status": {
13 | "description": "Support and lifecycle status of the versioned endpoint.",
14 | "type": "string",
15 | "enum": [
16 | "CURRENT",
17 | "SUPPORTED",
18 | "EXPERIMENTAL",
19 | "DEPRECATED"
20 | ]
21 | },
22 | "id": {
23 | "description": "The major API version.",
24 | "type": "string",
25 | "pattern": "^v[0-9]{1,2}.?[0-9]{0,2}$"
26 | },
27 | "links": {
28 | "$ref": "http://json-schema.org/draft-04/links#"
29 | },
30 | "max_version": {
31 | "desciption": "The maximum microversion available",
32 | "type": "string",
33 | "pattern": "^[0-9]{1,2}.[0-9]{1,2}$"
34 | },
35 | "min_version": {
36 | "desciption": "The minimum microversion available",
37 | "type": "string",
38 | "pattern": "^[0-9]{1,2}.[0-9]{1,2}$"
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/guidelines/versioned-discovery-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "id": "https://specs.openstack.org/openstack/api-wg/_downloads/versioned-discovery-schema.json#",
4 | "type": "object",
5 | "required": ["version"],
6 | "additionalProperties": false,
7 | "properties": {
8 | "version": {
9 | "$ref": "version-information-schema.json#"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # The order of packages is significant, because pip processes them in the order
2 | # of appearance. Changing the order has an impact on the overall integration
3 | # process, which may cause wedges in the gate later.
4 | pbr>=2.0
5 | doc8>=0.6.0 # Apache-2.0
6 | openstackdocstheme>=2.2.1 # Apache-2.0
7 | sphinx>=2.0.0,!=2.1.0 # BSD
8 | stestr>=2.0.0 # Apache-2.0
9 | testtools>=0.9.36,!=1.2.0
10 | yasfb>=0.8.0
11 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = api-sig
3 | summary = OpenStack API Special Interest Group Guidelines
4 | description-file =
5 | README.rst
6 | author = OpenStack
7 | author-email = openstack-discuss@lists.openstack.org
8 | home-page = http://www.openstack.org/
9 | classifier =
10 | Intended Audience :: Developers
11 | License :: OSI Approved :: Apache Software License
12 | Operating System :: POSIX :: Linux
13 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 | # implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
18 | import setuptools
19 |
20 | setuptools.setup(
21 | setup_requires=['pbr'],
22 | pbr=True)
23 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openstack/api-sig/91f190cd6ad63ee28ea55d6d7a1532ca91acb8ce/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_titles.py:
--------------------------------------------------------------------------------
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 | # not use this file except in compliance with the License. You may obtain
3 | # a copy of the License at
4 | #
5 | # http://www.apache.org/licenses/LICENSE-2.0
6 | #
7 | # Unless required by applicable law or agreed to in writing, software
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 | # License for the specific language governing permissions and limitations
11 | # under the License.
12 |
13 | import glob
14 | import os
15 |
16 | import docutils.core
17 | import testtools
18 |
19 |
20 | class TestTitles(testtools.TestCase):
21 | def _get_title(self, section_tree):
22 | section = {
23 | 'subtitles': [],
24 | }
25 | for node in section_tree:
26 | if node.tagname == 'title':
27 | section['name'] = node.rawsource
28 | elif node.tagname == 'section':
29 | subsection = self._get_title(node)
30 | section['subtitles'].append(subsection['name'])
31 | return section
32 |
33 | def _get_titles(self, spec):
34 | titles = {}
35 | for node in spec:
36 | if node.tagname == 'section':
37 | # Note subsection subtitles are thrown away
38 | section = self._get_title(node)
39 | titles[section['name']] = section['subtitles']
40 | return titles
41 |
42 | def _check_titles(self, filename, expect, actual):
43 | missing_sections = [x for x in expect.keys() if x not in actual.keys()]
44 | extra_sections = [x for x in actual.keys() if x not in expect.keys()]
45 |
46 | msgs = []
47 | if len(missing_sections) > 0:
48 | msgs.append("Missing sections: %s" % missing_sections)
49 | if len(extra_sections) > 0:
50 | msgs.append("Extra sections: %s" % extra_sections)
51 |
52 | for section in expect.keys():
53 | missing_subsections = [x for x in expect[section]
54 | if x not in actual[section]]
55 | # extra subsections are allowed
56 | if len(missing_subsections) > 0:
57 | msgs.append("Section '%s' is missing subsections: %s"
58 | % (section, missing_subsections))
59 |
60 | if len(msgs) > 0:
61 | self.fail("While checking '%s':\n %s"
62 | % (filename, "\n ".join(msgs)))
63 |
64 | def test_template(self):
65 | filenames = glob.glob("guidelines/*")
66 | for filename in filenames:
67 | if filename.endswith('~'):
68 | continue
69 | if os.path.isdir(filename):
70 | continue
71 | self.assertTrue(
72 | filename.endswith(".rst") or filename.endswith(".json"),
73 | "guideline file must use 'rst' or 'json'"
74 | "extension: {filename}".format(filename=filename))
75 | with open(filename) as f:
76 | data = f.read()
77 |
78 | docutils.core.publish_doctree(data)
79 |
--------------------------------------------------------------------------------
/tools/add-reviewers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 | import json
5 | import logging
6 | import subprocess
7 | import sys
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | def parse_args():
12 | parser = argparse.ArgumentParser(
13 | description="Add the cross-project liaisons as reviewers " \
14 | "on an API Special Interest Group review.")
15 | parser.add_argument('--debug', help="Print debugging information",
16 | action='store_true')
17 | parser.add_argument("username", help="Your Gerrit username", type=str)
18 | parser.add_argument("review", help="An API-SIG Gerrit review", type=str)
19 | args = parser.parse_args()
20 |
21 | return (args.debug, args.username, args.review)
22 |
23 | def get_liaisons():
24 | with open('doc/source/liaisons.json') as f:
25 | liaisons = json.load(f)['liaisons']
26 |
27 | names = [liaison['name'] for liaison in liaisons if liaison['name']]
28 |
29 | return names
30 |
31 | def add_reviewers(debug, username, liaisons, review):
32 | gerrit = [
33 | 'ssh',
34 | '-p',
35 | '29418',
36 | '{}@review.openstack.org'.format(username),
37 | 'gerrit',
38 | 'set-reviewers'
39 | ]
40 |
41 | for liaison in liaisons:
42 | # Hack to avoid six
43 | if sys.version_info.major < 3:
44 | liaison = liaison.encode('utf-8')
45 | gerrit.append('--add "{}"'.format(liaison))
46 |
47 | gerrit.append('{}'.format(review))
48 |
49 | logger.debug(' '.join(gerrit))
50 |
51 | subprocess.call(gerrit)
52 |
53 | if __name__ == '__main__':
54 | debug, username, review = parse_args()
55 |
56 | level = logging.INFO
57 | if debug:
58 | level = logging.DEBUG
59 |
60 | logging.basicConfig(
61 | level=level,
62 | format='%(levelname)s: %(message)s')
63 |
64 | liaisons = get_liaisons()
65 | add_reviewers(debug, username, liaisons, review)
66 |
67 | print("Added {} reviewers to {}".format(len(liaisons), review))
68 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | # Hold back to 1.4, since that's what's in Fedora 20 repos
3 | # and we don't need anything newer for nova-specs tests
4 | minversion = 1.4
5 | envlist = docs,py37,linters
6 | skipsdist = True
7 |
8 | [testenv]
9 | basepython = python3
10 | usedevelop = True
11 | setenv = VIRTUAL_ENV={envdir}
12 | deps = -r{toxinidir}/requirements.txt
13 | commands = stestr run --slowest {posargs}
14 |
15 | [testenv:linters]
16 | whitelist_externals = bash
17 | commands =
18 | # Check the *.rst files
19 | doc8 README.rst guidelines
20 | # Check the JSON files
21 | bash -c 'for f in guidelines/*.json; do echo Checking "$f"; python -m json.tool "$f" /dev/null || exit 1; done'
22 |
23 | [testenv:venv]
24 | commands = {posargs}
25 |
26 | [testenv:docs]
27 | commands =
28 | sphinx-build -W -b html -d doc/build/doctrees doc/source doc/build/html
29 |
--------------------------------------------------------------------------------